Является ли законным ссылаться на переменную в ее определении?

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

Весь код, который у вас есть, совершенно легален. Есть даже обстоятельства, в которых вы, возможно, даже захотите это сделать.