一般程序都是会从main函数开始进行,但事实上 main 函数之前也发生了很多操作。在 main 函数开始前,分成两部分 “系统调用部分” 和 “C++ 程序自身的部分”
main() 执行前
入口函数对运行库和程序运行环镜进行初始化,包括 堆、I/O、线程、全局变量构造等等。
入口函数完成初始化后,调用 main 函数,正式开始执行程序主体部分。
main函数执行之前,主要就是初始化系统相关资源: 1.设置栈指针 2.初始化static静态和global全局变量,即data段的内容 3.将未初始化部分的赋初值:数值型short,int,long等为0,bool为FALSE,指针为NULL,等等,即.bss段的内容 4.将main函数的参数,argc,argv等传递给main函数,然后才真正运行main函数
main运行前可运行哪些代码
1.全局对象的构造函数会在 main 函数之前执行。
例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class simpleClass {public : simpleClass ( ){ cout << "simpleClass constructor.." << endl; } }; simpleClass g_objectSimple; int _tmain(int argc, _TCHAR* argv[]){ return 0 ; }
2. 全局变量、对象和静态变量、对象的空间分配和赋初值
发生在执行main函数之前,而main函数执行完后, 还要去执行一些诸如释放空间、释放资源使用权等操作
静态变量构建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class simpleClass {public : simpleClass ( ){ cout << "simpleClass constructor.." << endl; } }; class simpleClassTwo {public : static simpleClass m_sSimpleClass; }; simpleClass simpleClassTwo::m_sSimpleClass = simpleClass (); int _tmain(int argc, _TCHAR* argv[]){ return 0 ; }
全局变量的赋值函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <iostream> using namespace std;int f () { printf ("before" ); return 0 ; }int A = f ();int main () { return 0 ; }
全局lambda变量调用
1 2 3 4 5 6 7 8 9 int a = []() { std::cout << "a" ; return 0 ; }();int main () { std::cout << "b" ; return 0 ; }
3.进程启动后,要执行一些初始化代码
(如设置环境变量等),然后跳转到main执行。全局对象的构造也在main之前
在写程序时,比如一个模块,通常要有 initialize 和 de-initialize,但是我们写 C 程序的时候为什么有些模块没有这两个过程么呢?比如我们程序从 main 开始就可以 malloc,free,但是我们在 main 里面却没有初始化堆。再比如在 main 里面可以直接 printf,可是我们并没有打开标准输出文件啊。
操作系统装载程序之后,首先运行的代码并不是main的第一行,而是某些特别的代码,这些代码准备好main函数执行说需要的环境,并且负责调用main函数,这时候你才可以再main函数里放心大胆的写各种代码:申请内存、使用系统调用、触发异常、访问IO。在main函数返回之后,他会记录main函数的返回值,调用atexit注册的函数,然后结束进程。 ——《程序员的自我修养–链接、装载与库》
那main() 执行后呢?
main函数执行完毕后,返回到入口函数,入口函数进行清理工作,包括全局变量的析构、堆销毁、关闭I/O等,然后系统调用结束进程。
main函数结束可以通过 return 0;或者 exit(0) 来结束,此时程序并非直接结束,而是先调用一些终止处理程序然后再结束。可以使用int atexit(void (*func)(void));来追加自定义终止处理程序,终止处理程序由 exit函数自动调用,调用顺序与登记顺序相反。
如果main函数发生了异常或者使用_exit和_Exit来退出程序,则不会调用终止处理程序。
微信的mars库中对运行在main前和main后的函数封装方法
运行前用全局变量和运行后用atexit函数, 使用BOOT_RUN_STARTUP的函数,在main运行前调用 使用BOOT_RUN_EXIT的函数,在main运行后调用
使用全局变量和atexit实现函数在main运行前和运行后运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #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; return 0 ; }
4. 通过关键字__attribute__
让一个函数在主函数之前运行,进行一些数据初始化、模块加载验证等。
如果是GNUC的编译器(gcc,clang),就在你要执行的方法前加上 attribute ((constructor))
1 2 3 4 5 6 7 8 9 #include <stdio.h> __attribute__((constructor)) void func () { printf ("hello world\n" ); }int main () { printf ("main\n" ); }
同理,如果想要在main函数结束之后运行, 可加上__attribute__((destructor))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <stdio.h> void func () { printf ("hello world\n" ); return 0 ; } __attribute__((constructor))void before () { printf ("before\n" ); func (); } __attribute__((destructor))void after () { printf ("after\n" ); }int main () { printf ("main\n" ); }