Какие операции могут потребоваться перед main()

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


person dabhaid    schedule 02.02.2011    source источник
comment
Вещи, которые не следует делать.   -  person R.. GitHub STOP HELPING ICE    schedule 03.02.2011
comment
Это было интересно: stackoverflow.com /вопросы/4783404/   -  person Dave    schedule 03.02.2011


Ответы (6)


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

Факт: нет.

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

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

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

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

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

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

Редактировать:

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

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

person Edward Strange    schedule 02.02.2011
comment
+1, это единственное применение, которое у меня есть для этого ... но, черт возьми, я его использую! - person Matthieu M.; 03.02.2011

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


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

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

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

person jschmier    schedule 02.02.2011

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

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

person R.. GitHub STOP HELPING ICE    schedule 03.02.2011

Вещи, сделанные перед основным:

  • В x86 регистр указателя стека обычно равен &=0XF3, чтобы сделать его кратным 4 (выравнивание).
  • Статические элементы инициализируются
  • push argc и argv (и окружение, если необходимо)
  • вызов _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
person KitsuneYMG    schedule 03.02.2011

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

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

person MSN    schedule 03.02.2011

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

person Tony Delroy    schedule 02.02.2011