最近碰到这样的一个问题:main函数的返回值的作用。
main函数的前世今生
每一个C程序都会有一个main函数作为入口点,这里所谓的入口点,也即程序一开始就会尝试找到main函数的位置,并从main函数的第一行代码开始执行。
那么,第一个问题:入口点之前是什么?main函数执行完之后,又是什么?这有点像:宇宙大爆炸之前的那一刻是什么状态?
我们来创建一个最简单的C程序,来看看入口点之前是什么?
如下图所示,我使用VS2010创建了一个控制台应用程序。
代码十分简单,我们在main函数中打印了一句hello,然后返回了0。
接下来,我们在printf函数处设置一个断点,然后F5启动调试。程序如期的在printf处中断了下来,我们先来看看调用堆栈。
在调用堆栈中,我们看到在main函数调用之前,有两次CRT库函数调用,具体代码如下:
从以上观察得出结论:系统CRT的库中的wmainCRTStartup/mainCRTStartup为实际用户程序的入口点,我们自己编写的main函数由CRT调用,并且,在调用之前CRT会尝试对一些底层基础设施进行初始化,包括默认的异常处理函数表,各种底层中断的处理策略等。这些基础性的工作完成之后,CRT调用我们的main函数进入应用程序的实际代码中。
好了,看了大爆炸之前的那一刻,我们来研究一下main函数执行完毕后,会发生什么?
还是继续进行单步调试,看看main函数返回后,代码如何走:
跟踪到最后,我们发现,CRT在收到main函数返回值后,将这个值传递了Win32API ExitProcess,当程序执行这个API时,操作系统将终止应用程序并释放整个进程资源,我们在main函数中准备的返回值,传递给了操作系统,操作系统会记录这个准备死亡的进程及其返回值,然后,进程死亡。
main返回值到底有什么用?
我们打开命令行窗口,分别让main返回0和77这两个不一样的值,然后使用%ERRORLEVEL%打印程序的退出代码,如下图:
我们可以看到,main函数的返回值,被用作了程序的退出代码。如果您在Linux下做测试,可以使用echo %?来打印应用程序的退出代码。
结论
1) main函数,从函数调用链来看,仅仅是又一个非常普通的C函数,但同时也不普通,它唯一的代表了应用程序的入口点。
2) main函数的返回值将被操作系统用作进程的退出代码,利用这个特性,我们可以使用返回值来标识某项任务是否成功执行,例如,如果任务成功执行,则返回0,否则,返回与具体错误相关的整数,我的习惯是:使用大于0的整数来标识各种可能发生的错误,因为,一旦涉及到负数(有符号),水就比较深了。