Преамбула
Программы на C и C++, как правило, проводят бо́льшую часть своей жизни внутри функции main()
и функций, прямо или косвенно вызываемых из main()
. Тем не менее, на самом деле выполнение программы начинается вовсе не с main()
, а с некоторого кода из стандартной библиотеки, поставляемой вместе с компилятором. Таковой код, по идее, должен подготавливать окружение для других функций стандартной библиотеки, которые, возможно, позовёт main()
, а также параметры самой main()
(под Windows; Unix-системы имеют тенденцию передавать argc/argv/envp
в подготовленном виде прямо при запуске процесса, но речь не о них). Симметрично, завершающий return
в функции main()
— вовсе не последняя инструкция программы, после него следует ещё немного кода из стандартной библиотеки.
В Visual Studio «настоящая» точка входа в программу называется mainCRTStartup
. В комплекте с VS идут исходники стандартной библиотеки, в VS2015 определение mainCRTStartup
находится в %PROGRAMFILES(X86)%VCcrtsrcvcruntimeexe_main.cpp
, но, впрочем, всю работу выполняет exe_common.inl
рядом. Давайте туда посмотрим.
...
// If this module has any thread-local destructors, register the
// callback function with the Unified CRT to run on exit.
_tls_callback_type const * const tls_dtor_callback = __scrt_get_dyn_tls_dtor_callback();
if (*tls_dtor_callback != nullptr && __scrt_is_nonwritable_in_current_image(tls_dtor_callback))
{
_register_thread_local_exe_atexit_callback(*tls_dtor_callback);
}
__telemetry_main_invoke_trigger(nullptr);
//
// Initialization is complete; invoke main...
//
int const main_result = invoke_main();
//
// main has returned; exit somehow...
//
__telemetry_main_return_trigger(nullptr);
if (!__scrt_is_managed_app())
exit(main_result);
if (!has_cctor)
_cexit();
// Finally, we terminate the CRT:
__scrt_uninitialize_crt(true, false);
return main_result;
...