Является ли законным ссылаться на переменную в ее определении?
int test[2] = { 45, test[0] };
int x = (x = 111);
cout << test[0] << " " << test[1] << " " << x << "\n"; // 45 45 111
Являются ли назначения в первых двух строках законными? Visual Studio 2010 компилирует и запускает его без каких-либо ошибок или предупреждений, но это похоже на нечетный случай, который может быть undefined, поэтому я хотел подтвердить его приемлемость. Visual Studio предупреждает меня, если я делаю что-то явно рефлексивное (и, предположительно, undefined), как int x = x;
, поэтому мне интересно, как справляются с этими ситуациями.
Ответы
Ответ 1
Из С++ Standard (С++ 11, но в С++ 98/03 он не отличался):
(§ 3.3.2/1) Точка объявления для имени сразу после его полного объявления (раздел 8) и перед его инициализатором (если есть), [...] [Пример:
int x = 12;
{ int x = x; }
Здесь второй x инициализируется с его собственным (неопределенным) значением. -end пример]
Это относится и к пользовательским типам, а также к типам массивов. Обратите внимание, что в стандарте подчеркивается, что x
во втором примере инициализируется неопределенным значением. Таким образом, нет способа узнать, какое значение x
инициализируется с помощью.
Ответ 2
Я предполагаю, что у вас есть какая-то функция, так как вы вызываете функции и т.д.
Пространство для test
и x
выделено в стеке . Теоретически пространство для этих парней должно существовать до того, как их значения будут заполнены. Если мы посмотрим на сгенерированную сборку (x86 gcc), это правда.
subl $40, %esp # Add 40 bytes of memory to the current stack
movl $0, -20(%ebp) # Clear test[0] to 0
movl $0, -16(%ebp) # Clear test[1] to 0
movl $45, -20(%ebp) # Place the value of 45 into test[0]
movl -20(%ebp), %eax # Copy that 45 into a register
movl %eax, -16(%ebp) # Move that register value (45) into test[1]
movl $111, -12(%ebp) # Assign x to be 111, optimize out the unnecessary duplicate assignment
... #continues on to set up and call printf
Мы видим, что в стек добавлено 40 байт. Обратите внимание, что адреса тестов [0], test [1] и x - все смежные адреса, помеченные от %ebp
с интервалами в 4 байта (-20, -16, -12, соответственно). Их расположение в памяти существует и может быть доступно без ошибок до того, как они будут определены. Здесь компилятор очищает их до 0, хотя мы видим, что это не нужно. Вы можете удалить эти две строки и все еще работать нормально.
Что мы можем сделать из этого, так это то, что ваш тег int [2] и int x мог иметь любое количество фанковых круговых ссылок внутри и код будет скомпилирован - это просто ваша работа, чтобы убедиться, что ваши ссылки захватывают хорошие данные (то есть какие-то инициализированные данные), а не мусор, которые вы здесь сделали. Это также работает с другими случаями - скомпилируйте сборку и убедитесь сами, как это делается.
Ответ 3
Это законно, потому что объявление переменной завершено к моменту ее инициализации. Однако значение test[1]
равно undefined.
Ответ 4
Весь код, который у вас есть, совершенно легален. Есть даже обстоятельства, в которых вы, возможно, даже захотите это сделать.