操作系统装载程序之后,首先运行的代码并不是main的第一行,而是某些特别的代码,这些代码准备好main函数执行所需要的环境,并且负责调用main函数,这时候你才可以在main函数里放心大胆的写各种代码:申请内存、使用系统调用、触发异常、访问IO。在main函数返回之后,他会记录main函数的返回值,调用atexit注册的函数,然后结束进程。
——《程序员的自我修养--链接、装载与库》
main前:
程序在执行时会调用各种各样的运行时库函数,因此执行前main函数前必须要初始化好运行时库,mainCRTStartup函数会负责相应的初始化工作,他会完成一些C全局变量以及C内存分配等初始化工作,在C++里,还要执行全局类对象的构造函数。最后,mainCRTStartup才调用main函数。main后:
在main函数返回之后,他会记录main函数的返回值,调用atexit注册的函数,然后结束进程。示例代码:
#include <QCoreApplication>
#include <QtDebug>
class Tmp {
public:
Tmp() {
qDebug() << Q_FUNC_INFO;
}
~Tmp() {
qDebug() << Q_FUNC_INFO;
}
};
int main_before() {
qDebug() << Q_FUNC_INFO;
return 0;
}
void main_after() {
qDebug() << Q_FUNC_INFO;
}
int doExit(void (*func)(void)) {
return atexit(func);
}
int nBefore = main_before();
int nAfter = doExit(main_after);
Tmp oTmp;
int main(int argc, char *argv[])
{
qDebug() << Q_FUNC_INFO;
// QCoreApplication app(argc, argv);
// return app.exec(); // 进入qt的事件循环
return 0;
}
- 运行结果
int main_before()
Tmp::Tmp()
int main(int, char**)
Tmp::~Tmp()
void main_after()
思考
感兴趣的同学可以把main函数里面注释掉的那两行代码放开,观察一下运行结果,然后思考一下全局对象的释放时机。封装
#ifdef __cplusplus
extern "C" {
#endif
__inline int boot_run_atstartup(void (*func)(void)) { func(); return 0;}
__inline int boot_run_atexit(void (*func)(void)) { return atexit(func);}
#ifdef __cplusplus
}
#endif
#define BOOT_RUN_STARTUP(func) VARIABLE_IS_NOT_USED static int __anonymous_run_variable_startup_##func = boot_run_atstartup(func)
#define BOOT_RUN_EXIT(func) VARIABLE_IS_NOT_USED static int __anonymous_run_variable_exit_##func = boot_run_atexit(func)