статический порядок инициализации фиаско - PullRequest
19 голосов
/ 14 июня 2010

Я читал о 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 ]

11 голосов
/ 14 июня 2010

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

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

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

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

11 голосов
/ 14 июня 2010

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

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

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

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

2 голосов
/ 14 июня 2010

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

1 голос
/ 14 июня 2010

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...