Run_Before_or_after_main
一般程序都是会从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. 全局变量、对象和静态变量、对象的空间分配和赋初值
发生在执行main函数之前,而main函数执行完后, 还要去执行一些诸如释放空间、释放资源使用权等操作
静态变量构建
1 |
|
全局变量的赋值函数
1 |
|
全局lambda变量调用
1 |
|
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 |
|
4. 通过关键字__attribute__
让一个函数在主函数之前运行,进行一些数据初始化、模块加载验证等。
如果是GNUC的编译器(gcc,clang),就在你要执行的方法前加上 attribute((constructor))
1 |
|
同理,如果想要在main函数结束之后运行, 可加上__attribute__((destructor))
1 |
|