Подтвердить что ты не робот

Порядок инициализации глобальной переменной С++

Я не понимаю, что делает следующий пример кода и как он это делает:

#include <stdio.h>

int f();

int a = f(); // a exists just to call f

int x = 22;

int f() {
    ++x;
    return 123; // unimportant arbitrary number
}

int main() {
    printf("%d\n", x);
}

Когда это выполняется, он печатает 23, что является интуитивным ответом.

Однако в С++ глобальные переменные должны быть инициализированы в порядке определения. Это означало бы, что a должен быть инициализирован до x, потому что он определен до x. Если это так, то функция f должна была бы быть вызвана до того, как был инициализирован x, потому что вызов f является частью определения a.

Если f действительно вызывается до инициализации x, это означает, что f будет пытаться увеличивать x - результат, на который я не уверен (скорее всего UB, или некоторое значение тарабарщины). Затем, после инициализации a, x будет инициализироваться на 22, и программа распечатает 22.

Очевидно, что не то, что происходит. Но что? Что делает этот код на самом деле?

Кажется, что x установлен в 22 до того, как оценивается a = f(), но это будет означать, что порядок инициализации отменяется (я также могу ошибиться в том, что такое инициализация или когда это произойдет).

4b9b3361

Ответ 1

Проблема немного тонкая; более подробно см. С++ 11 3.6.2.

Для нас важно, что есть две фазы инициализации "нелокальных переменных со статической продолжительностью хранения" (или "глобальных переменных" в разговорной речи): статическая фаза инициализации и фаза динамической инициализации. Сначала статическая фаза. Это выглядит так:

int a = 0;
int x = 22;

Динамическая инициализация запускается впоследствии:

a = f();

Дело в том, что статическая инициализация вообще не запускается - она ​​состоит только из значений, которые известны во время компиляции, поэтому эти значения уже установлены до того, как произойдет какое-либо выполнение. То, что делает инициализацию int x = 22; static, заключается в том, что инициализатор является константным выражением.


Бывают случаи, когда динамическая инициализация может быть вставлена ​​на статическую фазу (но не обязательно), но это не один из этих случаев, поскольку она не соответствует требованию, что

динамическая версия инициализации не изменяет значение любого другого объекта области пространства имен до его инициализации

Когда этот подъем происходит, допустимо, что исходные значения могут отличаться от того, если этого не произошло. Вот пример в стандарте для одной такой "неопределенной" инициализации.

Ответ 2

Также рассмотрим:

#include <iostream>
using namespace std;

int f();
int g();

int a = f();
int b = g();

int main() {
        cout << a << " " << b;
}

int f() {
        b++;
        cout << "f" << endl;
        return 1;
}

int g() {
        cout << "g" << endl;
        return 2;
}

Вывод:

f
g
1 2

Замена b = g(); на b = 22; вызывает печать 1 23. Ответ Kerrek SB объясняет, почему это так.

Ответ 3

Вы всегда можете переписать свою программу следующим образом:

#include <stdio.h>

int f(int x) {
    ++x;
    return x;
}

int main() {
    printf("%d\n", f(22));
}