Чтение онлайн

на главную - закладки

Жанры

Программирование на Visual C++. Архив рассылки

Jenter Алекс

Шрифт:

• Получаются значения аргументов командной строки программы и переменных среды.

• В случае необходимости, происходит инициализация консоли и привязка стандартного вывода к файловым дескрипторам C. При старте исполняемого файла, у которого в уже упомянутом заголовке PE значение поля Subsystem равно 3 (Windows character-mode executable), создается консоль. Это значение можно задать опцией линкера /subsystem. Выбор подсистемы выполнения также влияет на выбор стартовой функции (если ее имя не задано явно). Умолчанием является "console".

• Происходит вызов цепочки функций инициализации CRT и конструкторов глобальных

переменных (подробнее об этом – в следующем разделе).

• И лишь после этого вызывается функция [w]main или [w]WinMain. Коротко можно сказать, что функция xxxCRTStartup вызывает соответствующую функцию xxx.

• Программа работает.

• Выполняется последовательность действий по очистке, к которой мы еще вернемся.

• И, наконец, происходит завершение процесса.

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

Так, например, при вызове компилятора в командной строке таким образом:

cl test.cpp user32.lib

мы получим консольную программу и сообщение "Hello from main" (вспомните, что говорилось об умолчаниях).

А вызвав компилятор вот так:

cl test.cpp user32.lib /link /entry:WinMainCRTStartup /subsystem:console

мы получим "чудо чудное": программу, у которой выполняется функция WinMain, но создается окно консоли.

Код инициализации глобальных переменных

Как в VC++ реализован вызов цепочки функций инициализации/завершения?

Наличие в программе хотя бы одной глобальной переменной – экземпляра класса – заставляет компилятор сделать следующее. Во-первых, он генерирует невидимую за пределами модуля функцию, в которой и выполняются необходимые действия – вычисляется значение инициализатора или вызывается конструктор. Далее создается специальная запись с указателем на эту функцию в сегменте с именем вида ".CRT$xxx". Детально разбирать формат именования сегмента мы не будем, сейчас важно только то, что все сегменты такого типа будут при сборке объединены в алфавитном порядке в один сегмент. Таким образом, в момент старта программы в памяти будет находиться массив указателей на функции, при вызове которых и произойдут необходимые действия. В стартовом коде CRT VC этим занимается функция _initterm.

А почему здесь используется термин "функции инициализации/завершения " вместо терминов "конструкторы/деструкторы"?

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

int len = strlen("Hello, world!");

Обработка в этом случае ничем не отличается от инициализации экземпляра класса имеющего конструктор.

Код завершения

Упомянув инициализацию CRT, нельзя умолчать о коде очистки, или завершения. В нем выполняются действия обратного характера (и, в том числе, деструкторы глобальных переменных). Что действительно заслуживает описания, так это то, что код очистки можно вызвать собственноручно. Да-да, он содержится в функции exit.

Если же не вызвать ее явно, то она вызовется после возврата из main/WinMain. Наиболее выразительную реализацию вышесказанного я встретил однажды в исходных файлах CRT компилятора WATCOM C++:

exit(main(__argv, __argc, __envp));

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

ПРИМЕЧАНИЕ

Вообще-то, exit (вернее, возможность ее прямого вызова) является, скорее, "пережитком" со времен программирования на C. При вызове этой функции из программы на C++ не выполнятся деструкторы для локальных переменных (что естественно, поскольку, в отличие от глобальных объектов, их деструкторы нигде не зарегистрированы). Кроме того, вызов exit из деструктора может привести к входу программы в бесконечный цикл, так что не злоупотребляйте этой функцией.

Со времен создания библиотеки языка C осталась и такая возможность, как регистрация цепочки обработчиков завершения с помощью функций atexit/_onexit. Функции, зарегистрированные вызовом atexit/_onexit, будут вызваны в ходе завершения программы в порядке, обратном порядку их регистрации. Для программы на C++ с этой целью лучше воспользоваться глобальными деструкторами.

На самом деле, в программе на VC регистрация деструкторов глобальных объектов также выполняется с помощью внутреннего вызова atexit после вызова конструктора. Это имеет довольно веские основания: если конструктор объекта вызван не был, то не будет вызван и его деструктор. Но, в любом случае, это – деталь реализации, на которую полагаться не стоит.

Внутри exit содержится вызов функции более низкого уровня – _exit. Ее вызов не приведет к вызову деструкторов и exit-обработчиков, а только выполнит самую необходимую очистку (не буду вдаваться в подробности, замечу только, что при этом вызываются C-терминаторы (функции из таблицы в сегментах "CRT$XT[A-Z]"), в частности, подчищается low-level i/o) и завершит программу вызовом функции Windows API ExitProcess.

И, наконец, функция abort является способом "пожарного" завершения программы. Она выводит диагностическое сообщение и также вызывает _exit для завершения процесса.

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

Уменьшаем размер выполняемого модуля

Но в нашем примере нет ничего, что потребовало бы использовать CRT. Более того, включив оптимизацию по размеру (/O1) и генерацию карты исполняемого файла (Generate Link Map, /Fm), можно заметить, что размер функции main – всего 23 байта. А размер выполняемого модуля составляет около 36 килобайт. Неужели нельзя его немного уменьшить?

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

Использование внешней библиотеки CRT

Откомпилируем нашу программу следующей командой:

cl /MD test.cpp user32.lib

Размер полученного в результате EXE-файла составляет около 16 килобайт. Что за чудеса? Куда делась половина исполняемого модуля? Неужели он "похудел" за счет исключения CRT?

Поделиться:
Популярные книги

Газлайтер. Том 8

Володин Григорий
8. История Телепата
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Газлайтер. Том 8

На Ларэде

Кронос Александр
3. Лэрн
Фантастика:
фэнтези
героическая фантастика
стимпанк
5.00
рейтинг книги
На Ларэде

Охота на попаданку. Бракованная жена

Герр Ольга
Любовные романы:
любовно-фантастические романы
5.60
рейтинг книги
Охота на попаданку. Бракованная жена

Кай из рода красных драконов

Бэд Кристиан
1. Красная кость
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Кай из рода красных драконов

Хозяйка Проклятой Пустоши. Книга 2

Белецкая Наталья
2. Хозяйка Проклятой Пустоши
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Хозяйка Проклятой Пустоши. Книга 2

Безумный Макс. Поручик Империи

Ланцов Михаил Алексеевич
1. Безумный Макс
Фантастика:
героическая фантастика
альтернативная история
7.64
рейтинг книги
Безумный Макс. Поручик Империи

Потусторонний. Книга 2

Погуляй Юрий Александрович
2. Господин Артемьев
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Потусторонний. Книга 2

Чапаев и пустота

Пелевин Виктор Олегович
Проза:
современная проза
8.39
рейтинг книги
Чапаев и пустота

Солнечный корт

Сакавич Нора
4. Все ради игры
Фантастика:
зарубежная фантастика
5.00
рейтинг книги
Солнечный корт

Лютая

Шёпот Светлана Богдановна
Любовные романы:
любовно-фантастические романы
6.40
рейтинг книги
Лютая

Ведьмак (большой сборник)

Сапковский Анджей
Ведьмак
Фантастика:
фэнтези
9.29
рейтинг книги
Ведьмак (большой сборник)

Наследие Маозари 4

Панежин Евгений
4. Наследие Маозари
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Наследие Маозари 4

Ученик

Губарев Алексей
1. Тай Фун
Фантастика:
фэнтези
5.00
рейтинг книги
Ученик

Начальник милиции. Книга 5

Дамиров Рафаэль
5. Начальник милиции
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Начальник милиции. Книга 5