2011-02-02 2 views
19

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

+1

Вещи, которые не должны быть выполнены. –

+3

Это было интересно: http: // stackoverflow.com/questions/4783404/is-main-really-start-of-a-c-program – Dave

ответ

14

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

Точка зрения: нет.

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

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

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

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

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

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

Edit:

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

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

+0

+1, это единственное, что у меня есть для этого ... но мальчик я его использую! –

5

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


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

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

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

1

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

3

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

  • На x86, стек регистр указателя обычно & = 0XF3, чтобы сделать его кратным 4 (выравнивание)
  • Статические члены инициализируются
  • толчок ARGC и ARGV (и Environ при необходимости)
  • вызова _Главная = р

г ++ 4.4 выдает следующую BEF если какой-либо из моих кодов испускается. Технически он вставляет его в верхнюю часть 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 
3

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

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

2

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

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

Смежные вопросы