Статическая инициализация

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

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

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

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

  • при компиляции файла file1.cpp компилятор оставляет y так, как он есть i.e не выделяет для него хранилище.
  • компилятор выделяет хранилище для x, но не инициализирует его.
  • При компиляции файла file2.cpp компилятор уходит из x, так как он не выделяет для него хранилище. Компилятор
  • выделяет хранилище для y, но не инициализирует его.
  • При связывании file1.o и file2.o теперь давайте сначала инициализировать файл file2.o, так что теперь: Получает ли x начальное значение 0? или не инициализируется?

Ответ 1

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

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

Ответ 4

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

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

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

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

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