Почему следующие компиляции без ошибок ?:Можно ли использовать переменную во время объявления?
int main()
{
int x = x; //I thought this should cause an error
return 0;
}
Где в стандартах это объясняется, почему это разрешено?
Почему следующие компиляции без ошибок ?:Можно ли использовать переменную во время объявления?
int main()
{
int x = x; //I thought this should cause an error
return 0;
}
Где в стандартах это объясняется, почему это разрешено?
С ++ 14 Проект N4140 [basic.scope.pdecl]/1:
точка декларации для имени сразу после его полного описателя (пункт 8), и до ее инициализатор (если есть), за исключением случаев, указанных ниже. [Примера:
unsigned char x = 12;
{ unsigned char x = x; }
Здесь второго
x
инициализируются со своим собственным (неопределенным) значением. -end пример]
Большая часть того, что вы цитируете, примечание, которое не является нормативным, и на самом деле это не объясняет, является ли это неопределенным поведением или нет. См. [Мой комментарий выше] (http://stackoverflow.com/questions/29525797/can-a-variable-be-used-while-being-declared#comment47210802_29525797) для двух вопросов, которые подробно отвечают на этот вопрос для C++ для обоих pre C++ 14 и C++ 14 и вперед. –
@ShafikYaghmour: вопрос заключается не в том, является ли это неопределенным поведением, может ли переменная использоваться в своей собственной инициализации. Существует множество случаев использования переменной без использования ее значения, например. 'void * p = & p;' поэтому это вполне разумный вопрос. –
@BenVoigt в случае с C++ этот пример явно неопределенного поведения и не говорит, что это плохой ответ IMHO. Конечно, есть оговорки, но ни один из них не объясняется. То, как работают современные оптимизирующие компиляторы, нужно очень четко понимать, что есть, а что нет UB b/c, оптимизаторы могут делать очень агрессивные вещи с кодом, который, по его мнению, вызывает UB. –
Ответ на C++ - я цитирую C++ 11 стандарт:
3.3.3.1:
Имя, объявленный в блоке (6.3) локально к этому блоку; он имеет блокировку. Его потенциальный объем начинается с момента его объявления (3.3.2) и заканчивается в конце его блока.
И точка декларации определяется в разделе 3.3.2, как
Точки декларации на имя сразу после его полного описателя (раздел 8) и до его инициализатора (если таковой имеется), за исключением случаев, указанных ниже.
[Пример:
int x = 12;
{ int x = x; }
Здесь вторые й инициализируются со своим собственным (неопределенным) значением. - конец примера]
Очевидно, что использование значения x
до его инициализации - это неопределенное поведение.
Этот вопрос имеет несколько иной ответ на языке C, чем на C++. В этом случае int x = x;
пытается инициализировать x
с собой.
В C++: [dcl.init]/12 (N3936) говорит, что любая оценка объекта с неопределенным значением вызывает неопределенное поведение, для некоторых случаев, связанных с беззнаковыми char
исключением.На самом деле существует Пример:
int f(bool b) {
unsigned char c;
unsigned char d = c; // OK, d has an indeterminate value
int e = d; // undefined behavior
return b ? d : 0; // undefined behavior if b is true
}
В C: Это является более сложным. Это очень похоже на поведение int b; foo(b - b);
, которое равно covered in full here.
Я не буду повторять этот текст, но выводы, в С11:
int a = a; &a;
вызывает UB тогда и только тогда, когда система имеет ловушку представления для int
int a = a;
, без последующего возникновения &a
, вызывает UB.Историческая справка: В C90 это вызвало UB. В C99 было введено ловушечное представление , а в C11 была введена возможность регистрации ловушки (для Itanium). Стандарт C++ вообще не касается представлений о ловушках, он, как представляется, не указан в случае таких вещей, как bitwise operators, генерирующий отрицательный ноль.
Паскаль дает очень хорошее резюме эволюции неопределенных значений в C в [Чтение неопределенного содержимого также может быть неопределенным] (http://blog.frama-c.com/index.php?post/2013/03/13/indeterminate-undefined) –
Хм - не следует объявлять 'a' с помощью ключевого слова volatile, выполняющего ту же работу, что и запрос адреса переменной (& a)? – AnArrayOfFunctions
@FISOCPP в стандарте не говорится, что –
Там действительно две части этого вопроса, один спросил:
Может переменная будет использоваться при декларируемых?
Там, где четко ответили Да, из-за положения правила объявления, цитируемого в других ответах.
И не менее важно, но не спросил:
Что Обычаи переменной во время ее объявления безопасны?
Ну, в инициализаторе переменная еще не завершила инициализацию (вопрос времени жизни объекта), и на самом деле конструкция еще не началась. Правила жизни объекта (раздел 3.8 Стандарта) утверждают, что некоторые, но не все операции разрешены на такой переменной:
До жизни объекта началось, но после хранения которого объект будет занимать было выделено или, после того, как срок жизни объекта закончился и перед хранилищем, которое объект занят, повторно используется или выпущен, может использоваться любой указатель, который ссылается на место хранения, в котором находится или находится объект, но только ограниченным образом. По строительству или разрушению объекта см. 12.7. В противном случае такой указатель относится к выделенному хранилищу и с использованием указателя, как если бы указатель имел тип
void*
, четко определен. Разрешено использование такого указателя, но полученное значение lvalue может использоваться только ограниченным образом, как описано ниже.Программа имеет неопределенное поведение, если:
- объект будет или была типа класса с нетривиальным деструктором и указатель используется в качестве операнда удаления-выражения используется
- указателя для доступа к нестатическому элементу данных или вызова нестатической функции-члена объекта, или
- указатель неявно преобразован в указатель на виртуальный базовый класс, или
- указатель используется в качестве операнда a
static_cast
, за исключением случаев, когда конверсия указана наcv
void
, или указатель наcv
void
, а затем на указатель либоcv
char
илиcv
unsigned char
или- указатель используется в качестве операнда
dynamic_cast
.
Эффективно для типов с нетривиальной инициализации, ячейка памяти не содержит объект еще, так что это не имеет никакого динамического типа, и пытается получить доступ к нему в качестве любого типа, за исключением char
или unsigned char
сразу падает aful строгого наложения.
Для типов с тривиальной инициализацией, включая int
, объект существует, как только правильно выровнено хранилище. Но если это хранилище имеет автоматическую или динамическую продолжительность хранения, значение неопределенно, пока переменная не будет записана. Это правило из раздела 8.5 применяется:
Если для объекта не задан инициализатор, объект инициализируется по умолчанию. Когда хранилище для объекта с автоматической или динамической продолжительностью хранения получается, объект имеет неопределенное значение, и если для объекта не выполняется инициализация, этот объект сохраняет неопределенное значение до тех пор, пока это значение не будет заменено. [Примечание. Объекты со статикой или длительностью хранения потоков нуль-инициализируются, см. 3.6.2. - конец примечание] Если неопределенное значение получают путем оценки, поведение не определено, за исключением следующих случаев:
и все исключительные случаи, перечисленные являются специфическими для unsigned char
.
На первый взгляд это правило не применяется, поскольку указан инициализатор. Тем не менее, во время оценки инициализатора мы точно в том случае, если «когда хранилище для объекта с автоматическим или динамическим временем хранения получается», где правило применяется.
Вы интересуетесь ответом для C++ или c? Возможно, что они разные, учитывая, что они имеют разные стандарты. – horns
Это неопределенное поведение. – Maroun
Еще одна веская причина включить предупреждения компилятора: '-Wall -Werror' - ваши друзья. – chqrlie