Jenter Алекс
Шрифт:
Исходя из этого, разработчики современных компиляторов C++ строят CRT таким образом, чтобы её стартовый код включался в программу по умолчанию. В большинстве случаев это – именно то поведение, которое требуется. В самом деле, большой проект на C++ редко обходится без использования CRT-функций или вычислений c плавающей точкой. Да и "довесок" в 30 Кб в таком случае невелик.
Если это вас устраивает, проблема с упомянутым ATL-проектом решается достаточно просто. Необходимо зайти в настройки проекта ("Project" – "Settings"), выбрать нужную Release-конфигурацию и на закладке "C++" удалить опцию препроцессора _ATL_MIN_CRT. Вопрос будет снят. Дальше можно не читать.
Но встречаются случаи, когда считаешь буквально каждый байт исполняемого модуля. Это может быть ядро инсталлятора или самораспаковывающегося архива, элемент управления ActiveX, который скачивается через Интернет, или приложение для встраиваемой системы. Компиляторы C++ (и Visual C++, в том числе), на мой взгляд, наиболее подходят для такого рода разработок. Приложение может, в конце концов, состоять из большого количества модулей, и мало что значащие 30 Кб могут превратиться в несколько сотен килобайт, а то и мегабайт. Но для контроля над процессом сборки придется погрузиться в некоторые детали реализации поддержки CRT.
Среди начинающих программистов можно услышать такое мнение: для консольной программы используется только функция main, а для оконной – WinMain. Это мнение, хотя и подтвержденное умолчаниями компилятора и линкера, в общем случае, является ошибочным.
Чтобы немного развлечься, проведем эксперимент. Создадим файл test.cpp:
Внимание, вопрос: что появится на экране после запуска такой программы? Постарайтесь ответить на этот вопрос, не заглядывая в дальнейшее описание.
ПРИМЕЧАНИЕ
Я не стал рассматривать еще два возможных варианта стартовой функции: wmain или wWinMain, предназначенных для проектов, компилируемых в Unicode. Кроме того, при создании DLL имеется еще один вариант стартовой функции – DllMain.
Функция [w]main или [w]WinMain, с которой начинается выполнение программы, вовсе не является точкой входа исполняемого модуля! На самом деле, программа на C++ начинает работу с выполнения специальной процедуры инициализации. Что касается Win32, то адрес этой процедуры и содержится в поле AddressOfEntryPoint заголовка portable executable (pe) выполняемого файла. Она представляет собой обычную функцию C, описанную с соглашением о вызовах __stdcall. В зависимости от настроек проекта, в Visual C++ эта функция может называться [w]mainCRTStartup, [w]WinMainCRTStartup или _DllMainCRTStartup (символ 'w' добавляется к имени для Unicode-проектов). Конкретно же для сборки приложения имя функции-точки входа можно задать опцией линкера /entry. Умолчанием для visual c++ является "maincrtstartup". Все сказанное справедливо и для некоторых других компиляторов C++ для Win32.
Что же происходит во время ее выполнения? Вот типичный сценарий работы такой функции (случай DLL здесь не рассматривается).
• Инициализируются переменные CRT (такие, как errno и osver). Многопоточная библиотека требует особой инициализации.
• Происходит инициализация динамической памяти (кучи).
• Инициализируется среда обработки ошибок в вычислениях с плавающей точкой. Это необходимо не только для библиотечных функций (таких, как sqrt), но и для преобразований между целочисленными и плавающими типами данных.
• Получаются значения аргументов командной строки программы и переменных среды.
• В случае необходимости, происходит инициализация консоли и привязка стандартного вывода к файловым дескрипторам C. При старте исполняемого файла, у которого в уже упомянутом заголовке PE значение поля Subsystem равно 3 (Windows character-mode executable), создается консоль. Это значение можно задать опцией линкера /subsystem. Выбор подсистемы выполнения также влияет на выбор стартовой функции (если ее имя не задано явно). Умолчанием является "console".
• Происходит вызов цепочки функций инициализации CRT и конструкторов глобальных переменных (подробнее об этом – в следующем разделе).
• И лишь после этого вызывается функция [w]main или [w]WinMain. Коротко можно сказать, что функция xxxCRTStartup вызывает соответствующую функцию xxx.
• Программа работает.
• Выполняется последовательность действий по очистке, к которой мы еще вернемся.
• И, наконец, происходит завершение процесса.