在 main函数之前发生了什么
对于编程人员来讲,main 函数是程序的入口,但事实上 main 函数之前也发生了很多操作。在 main 函数开始前,分成两部分 “系统调用部分” 和 “C++ 程序自身的部分”。
我们首先假设程序的 main 函数原型是int main(int argc, char *argv[]);
,其中,argc 指命令行参数的数目, argv 是指向参数的各个指针所构成的数组。
系统调用部分
对于 Linux 系统而言,当内核执行 C 程序时使用 exec 函数,在调用 main 前先调用一个特殊的启动例程。可执行文件将此启动例程指定为程序的起始地址。启动例程从内核取得命令行参数和环境变量值。
- 简而言之,系统会为你设置栈,并且将
argc,argv
和envp
压入栈中。文件描述符0,1和2(stdin, stdout和stderr)
保留shell之前的设置。 - 加载器会帮你完成重定位,调用你设置的预初始化函数。
- 当所有搞定之后,控制权会传递给
_start()
,即程序的入口函数
程序本身的 main() 执行前
- 入口函数对运行库和程序运行环镜进行初始化,包括 堆、I/O、线程、全局变量构造等等。
- 入口函数完成初始化后,调用 main 函数,正式开始执行程序主体部分。
main() 结束之后呢?
- main函数执行完毕后,返回到入口函数,入口函数进行清理工作,包括全局变量的析构、堆销毁、关闭I/O等,然后系统调用结束进程。
- main函数结束可以通过
return 0;
或者exit(0)
来结束,此时程序并非直接结束,而是先调用一些终止处理程序然后再结束。可以使用int atexit(void (*func)(void));
来追加自定义终止处理程序,终止处理程序由exit
函数自动调用,调用顺序与登记顺序相反。 - 如果main函数发生了异常或者使用
_exit
和_Exit
来退出程序,则不会调用终止处理程序。