2015-04-08 6 views
5

Почему следующие компиляции без ошибок ?:Можно ли использовать переменную во время объявления?

int main() 
{ 
    int x = x; //I thought this should cause an error 
    return 0; 
} 

Где в стандартах это объясняется, почему это разрешено?

+0

Вы интересуетесь ответом для C++ или c? Возможно, что они разные, учитывая, что они имеют разные стандарты. – horns

+2

Это неопределенное поведение. – Maroun

+0

Еще одна веская причина включить предупреждения компилятора: '-Wall -Werror' - ваши друзья. – chqrlie

ответ

3

С ++ 14 Проект N4140 [basic.scope.pdecl]/1:

точка декларации для имени сразу после его полного описателя (пункт 8), и до ее инициализатор (если есть), за исключением случаев, указанных ниже. [Примера:

unsigned char x = 12; 
{ unsigned char x = x; } 

Здесь второго x инициализируются со своим собственным (неопределенным) значением. -end пример]

+0

Большая часть того, что вы цитируете, примечание, которое не является нормативным, и на самом деле это не объясняет, является ли это неопределенным поведением или нет. См. [Мой комментарий выше] (http://stackoverflow.com/questions/29525797/can-a-variable-be-used-while-being-declared#comment47210802_29525797) для двух вопросов, которые подробно отвечают на этот вопрос для C++ для обоих pre C++ 14 и C++ 14 и вперед. –

+0

@ShafikYaghmour: вопрос заключается не в том, является ли это неопределенным поведением, может ли переменная использоваться в своей собственной инициализации. Существует множество случаев использования переменной без использования ее значения, например. 'void * p = & p;' поэтому это вполне разумный вопрос. –

+0

@BenVoigt в случае с C++ этот пример явно неопределенного поведения и не говорит, что это плохой ответ IMHO. Конечно, есть оговорки, но ни один из них не объясняется. То, как работают современные оптимизирующие компиляторы, нужно очень четко понимать, что есть, а что нет UB b/c, оптимизаторы могут делать очень агрессивные вещи с кодом, который, по его мнению, вызывает UB. –

3

Ответ на C++ - я цитирую C++ 11 стандарт:

3.3.3.1:

Имя, объявленный в блоке (6.3) локально к этому блоку; он имеет блокировку. Его потенциальный объем начинается с момента его объявления (3.3.2) и заканчивается в конце его блока.

И точка декларации определяется в разделе 3.3.2, как

Точки декларации на имя сразу после его полного описателя (раздел 8) и до его инициализатора (если таковой имеется), за исключением случаев, указанных ниже.

[Пример:

int x = 12; 
{ int x = x; } 

Здесь вторые й инициализируются со своим собственным (неопределенным) значением. - конец примера]

Очевидно, что использование значения x до его инициализации - это неопределенное поведение.

+2

«Использование» 'x' может включать' sizeof x' или '& x'. Оба они не являются явно неопределенным поведением. – chux

+1

@chux Согласен - я изменил ответ. –

+0

Да, я подозреваю, что даже 'int x = x - x;' даже не безопасен. – chux

6

Этот вопрос имеет несколько иной ответ на языке 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, генерирующий отрицательный ноль.

+1

Паскаль дает очень хорошее резюме эволюции неопределенных значений в C в [Чтение неопределенного содержимого также может быть неопределенным] (http://blog.frama-c.com/index.php?post/2013/03/13/indeterminate-undefined) –

+0

Хм - не следует объявлять 'a' с помощью ключевого слова volatile, выполняющего ту же работу, что и запрос адреса переменной (& a)? – AnArrayOfFunctions

+0

@FISOCPP в стандарте не говорится, что –

0

Там действительно две части этого вопроса, один спросил:

Может переменная будет использоваться при декларируемых?

Там, где четко ответили Да, из-за положения правила объявления, цитируемого в других ответах.

И не менее важно, но не спросил:

Что Обычаи переменной во время ее объявления безопасны?

Ну, в инициализаторе переменная еще не завершила инициализацию (вопрос времени жизни объекта), и на самом деле конструкция еще не началась. Правила жизни объекта (раздел 3.8 Стандарта) утверждают, что некоторые, но не все операции разрешены на такой переменной:

До жизни объекта началось, но после хранения которого объект будет занимать было выделено или, после того, как срок жизни объекта закончился и перед хранилищем, которое объект занят, повторно используется или выпущен, может использоваться любой указатель, который ссылается на место хранения, в котором находится или находится объект, но только ограниченным образом. По строительству или разрушению объекта см. 12.7. В противном случае такой указатель относится к выделенному хранилищу и с использованием указателя, как если бы указатель имел тип void*, четко определен. Разрешено использование такого указателя, но полученное значение lvalue может использоваться только ограниченным образом, как описано ниже.Программа имеет неопределенное поведение, если:

  • объект будет или была типа класса с нетривиальным деструктором и указатель используется в качестве операнда удаления-выражения используется
  • указателя для доступа к нестатическому элементу данных или вызова нестатической функции-члена объекта, или
  • указатель неявно преобразован в указатель на виртуальный базовый класс, или
  • указатель используется в качестве операнда a static_cast, за исключением случаев, когда конверсия указана на cvvoid, или указатель на cvvoid, а затем на указатель либо cvchar или cvunsigned char или
  • указатель используется в качестве операнда dynamic_cast.

Эффективно для типов с нетривиальной инициализации, ячейка памяти не содержит объект еще, так что это не имеет никакого динамического типа, и пытается получить доступ к нему в качестве любого типа, за исключением char или unsigned char сразу падает aful строгого наложения.

Для типов с тривиальной инициализацией, включая int, объект существует, как только правильно выровнено хранилище. Но если это хранилище имеет автоматическую или динамическую продолжительность хранения, значение неопределенно, пока переменная не будет записана. Это правило из раздела 8.5 применяется:

Если для объекта не задан инициализатор, объект инициализируется по умолчанию. Когда хранилище для объекта с автоматической или динамической продолжительностью хранения получается, объект имеет неопределенное значение, и если для объекта не выполняется инициализация, этот объект сохраняет неопределенное значение до тех пор, пока это значение не будет заменено. [Примечание. Объекты со статикой или длительностью хранения потоков нуль-инициализируются, см. 3.6.2. - конец примечание] Если неопределенное значение получают путем оценки, поведение не определено, за исключением следующих случаев:

и все исключительные случаи, перечисленные являются специфическими для unsigned char.

На первый взгляд это правило не применяется, поскольку указан инициализатор. Тем не менее, во время оценки инициализатора мы точно в том случае, если «когда хранилище для объекта с автоматическим или динамическим временем хранения получается», где правило применяется.

Смежные вопросы