Статический порядок инициализации фиаско

Я читал о SIOF из книги, и она привела пример:

//file1.cpp
extern int y;
int x=y+1;

//file2.cpp
extern int x;
int y=x+1;  

Теперь мой вопрос:
В приведенном выше коде будут происходить следующие вещи?

  1. во время компиляции file1.cpp компилятор оставляет y как есть, т.е. не выделяет для него память.
  2. Компилятор выделяет память для x, но не инициализирует ее.
  3. При компиляции file2.cpp компилятор оставляет x как есть, т.е. не выделяет для него память.
  4. компилятор выделяет память для y, но не инициализирует ее.
  5. При связывании file1.o и file2.o теперь пусть сначала инициализируется file2.o, так что теперь:
    Получает ли x начальное значение 0? или не инициализируется?

4 ответа

Решение

Шаги инициализации приведены в 3.6.2 "Инициализация нелокальных объектов" стандарта C++:

Шаг 1: x а также y инициализируются нулями перед любой другой инициализацией.

Шаг 2: x или же y динамически инициализируется - который не указан стандартом. Эта переменная получит значение 1 поскольку другая переменная будет инициализирована нулями.

Шаг 3: другая переменная будет динамически инициализирована, получая значение 2,

SIOF в значительной степени является артефактом времени выполнения, компилятор и компоновщик не имеют к нему никакого отношения. Рассмотрим функцию atexit(), она регистрирует функции, вызываемые при выходе из программы. Во многих реализациях CRT есть что-то похожее для инициализации программы, назовем это atinit().

Инициализация этих глобальных переменных требует выполнения кода, значение не может быть определено компилятором. Таким образом, компилятор генерирует фрагменты машинного кода, которые выполняют выражение, и присваивает значение. Эти фрагменты должны быть выполнены до запуска main().

Вот где atinit () вступает в игру. Обычная реализация CRT просматривает список указателей функций atinit и выполняет фрагменты инициализации по порядку. Проблема заключается в порядке, в котором функции зарегистрированы в списке atinit(). Хотя atexit() имеет четко определенный порядок LIFO и неявно определяется порядком, в котором код вызывает atexit(), это не относится к функциям atinit. Спецификация языка не требует заказа, вы ничего не можете сделать в своем коде, чтобы указать заказ. SIOF является результатом.

Одной из возможных реализаций является указатель на функцию, генерирующую компилятор в отдельном разделе. Компоновщик объединяет их, создавая список atinit. Если ваш компилятор делает это, то порядок инициализации будет определяться порядком, в котором вы связываете объектные файлы. Посмотрите на файл карты, вы должны увидеть раздел atinit, если ваш компилятор делает это. Он не будет называться atinit, но скорее всего будет какое-то имя с именем "init". Взгляд на исходный код CRT, который вызывает main(), также должен дать понимание.

Весь смысл (и причина, по которой это называется "фиаско") состоит в том, что невозможно с уверенностью сказать, что произойдет в таком случае, как этот. По сути, вы просите о чем-то невозможном (две переменные должны быть больше одной). Так как они не могут сделать это, то, что они будут делать, открыто для какого-то вопроса - они могут выдать 0/1, или 1/0, или 1/2, или 2/1, или, возможно (в лучшем случае), просто ошибку сообщение.

Это зависит от компилятора и может зависеть от времени выполнения. Компилятор может решить лениво инициализировать статические переменные при обращении к первой переменной в файле или при обращении к каждой переменной. В противном случае он будет инициализировать все статические переменные по файлам во время запуска, причем порядок обычно зависит от порядка ссылок файлов. Порядок файлов может меняться в зависимости от зависимостей или других зависимых от компилятора влияний.

Статические переменные обычно инициализируются нулями, если они не имеют константы инициализатора. Опять же, это зависит от компилятора. Таким образом, одна из этих переменных, вероятно, будет равна нулю при инициализации другой. Однако, поскольку оба имеют инициализаторы, некоторые компиляторы могут оставить значения неопределенными.

Я думаю, что наиболее вероятный сценарий будет:

  1. Пространство выделено для переменных, и оба имеют значение 0.
  2. Одна переменная, скажем, x, инициализируется и устанавливается в значение 1.
  3. Другой, скажем, у, инициализируется и устанавливается в значение 2.

Вы всегда можете запустить его и посмотреть. Может случиться так, что некоторые компиляторы будут генерировать код, который входит в бесконечный цикл.

Другие вопросы по тегам