Какие операции нужно выполнить перед main () - PullRequest
19 голосов
/ 03 февраля 2011

Я сталкивался с этим вопросом , спрашивающим, как выполнить код перед main () в C, упоминая, что были стратегии для C ++. Я в основном жил в пространстве приложений, поэтому выполнение до main () мне никогда не приходило в голову. Какие вещи требуют этой техники?

Ответы [ 6 ]

14 голосов
/ 03 февраля 2011

«Какие вещи требуют этой техники?»

Факт: нет.

Тем не менее, существует множество полезных вещей, которые вы ХОТИТЕ делать перед главным по разным причинам. В качестве одного практического примера, скажем, у вас есть абстрактная фабрика, которая создает doohickies. Вы можете убедиться, что создаете экземпляр фабрики, назначаете его для какой-то особой области, а затем регистрируете для него различные конкретные функции ... да, вы можете это сделать.

С другой стороны, если вы реализуете фабрику как одиночную и используете факты глобальной инициализации значений, чтобы «обмануть» реализацию в регистрации конкретных дурацких команд перед запуском main, вы получите несколько преимуществ при очень небольших затратах (факт использования синглтоны, в основном здесь не проблема, почти единственная).

Например, вы:

  1. Не нужно вести список регистраций, которые все должны называться явно. Фактически, вы можете даже объявить и определить целый класс в закрытой области видимости, недоступном для кого-либо, и иметь его доступным для использования при запуске программы.

  2. main () не должен делать кучу дерьма с кучей объектов, о которых он не заботится.

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

Edit:

Следует отметить, что с тех пор я узнал, что это не гарантируется языком. C ++ только гарантирует, что инициализация ноль или константа произойдет до main. В этом ответе я говорю о динамической инициализации. Это гарантия C ++ происходит перед первым использованием переменной, во многом как функциональные локальные статические переменные.

Кажется, что каждый компилятор выполняет динамическую инициализацию перед main. Я думал, что однажды столкнулся с тем, чего не произошло, но я думаю, что источником проблемы было что-то другое.

5 голосов
/ 03 февраля 2011

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


GCC предоставляет конструктор и деструктор атрибуты функции , которые вызывают автоматический вызов функции до того, как выполнение будет введено main() или main() завершено или exit() вызвано соответственно.

void __attribute__ ((constructor)) my_init(void);
void __attribute__ ((destructor)) my_fini(void);

В случае инициализации библиотеки подпрограммы конструктора выполняются до возврата dlopen(), если библиотека загружена во время выполнения, или до запуска main(), если библиотека загружается во время загрузки.При использовании для очистки библиотеки процедуры деструктора выполняются до того, как dlclose() вернется, если библиотека загружена во время выполнения или после exit() или при завершении main(), если библиотека загружена во время загрузки.

3 голосов
/ 03 февраля 2011

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

Одним из возможных «исключений» является инициализация глобальных константных таблиц во время выполнения.Но это очень плохая практика, поскольку таблицы не разделяются между экземплярами библиотеки / процесса, если вы заполняете их во время выполнения.Гораздо умнее написать скрипт для генерации таблиц static const в виде исходного файла на C или C ++ во время сборки.

3 голосов
/ 03 февраля 2011

Материал сделан до основного:

  • На x86 регистр указателя стека обычно равен & = 0XF3, что делает его кратным 4 (выравнивание)
  • Статические члены инициализируются
  • push argc и argv (и окружение, если необходимо)
  • call _main = p

g ++ 4.4 выдает следующее до того, как будет выпущен любой мой код. Технически он вставляет его в верхнюю часть main перед любым моим кодом, но я видел компиляторы, которые используют _init вместо _main в качестве точки входа:

.cfi_startproc
.cfi_personality 0x3,__gxx_personality_v0
pushq   %rbp
.cfi_def_cfa_offset 16
movq    %rsp, %rbp
.cfi_offset 6, -16
.cfi_def_cfa_register 6
subq    $16, %rsp
movl    %edi, -4(%rbp)
movq    %rsi, -16(%rbp)
# My code follows
2 голосов
/ 03 февраля 2011

Все, что нужно для запуска кода, чтобы гарантировать инварианты для вашего кода после запуска main, должно выполняться до main.Такие вещи, как глобальные iostreams, библиотека времени выполнения C, привязки ОС и т. Д.

Теперь, действительно ли вам нужно написать код, который также выполняет такие вещи - это то, что все остальные отвечают.

1 голос
/ 03 февраля 2011

Если у вас есть библиотека, очень удобно иметь возможность инициализировать некоторые данные, создавать потоки и т. Д. Перед вызовом main () и знать, что желаемое состояние достигается, не обременяя и не доверяя клиентскому приложению явный вызов некоторый код инициализации и / или завершения работы библиотеки. Внешне это может быть достигнуто наличием статического объекта, конструктор и деструктор которого выполняют необходимые операции. К сожалению, несколько статических объектов в разных единицах перевода или библиотеках будут иметь неопределенный порядок инициализации, поэтому, если они зависят друг от друга (еще хуже, циклически), то они могут все еще не достичь своего инициализированного состояния до того, как поступит запрос in. Аналогично, один статический объект может создавать потоки и вызывать службы в другом объекте, который еще не является потокобезопасным. Таким образом, для устойчивости к произвольному использованию необходим более структурированный подход с надлежащими одноэлементными экземплярами и блокировками, и все это выглядит гораздо менее привлекательным, хотя в некоторых случаях это все же может быть чистый выигрыш.

...