郭妍
C和C++可以说是所有编程语言中关系最为紧密的两个。在目标上,C++被定位为“a better C”;在名称上,C++有一个别名叫做“C with classes”;在语法上,C更是C++的一个子集,C++几乎支持C语言的全部功能。如何优化C++程序代码,在多年的教学经验中,我认为有一种方法,即不要让main函数返回void。
同C程序一样,每个C++程序都包含一个或多个函数,而且必须有一个函数命名为main,并且每个函数都由具有一定功能的语句序列组成。操作系统将main作为程序入口,调用main函数执行程序;main函数执行其语句序列,并返回一个值给操作系统。在大多数系统中,main函数的返回值用于说明程序的退出状态。如果返回0,则代表main函数成功执行完毕,程序正常退出,否则代表程序异常退出。
然而在编写C++程序入口函数main的时候,很多程序员,特别是一些具有C基础的C++程序员时经常会写出如下格式的main函数:
1.void main()
2.{
3.// some code... }
上述代码在VC++中是可以正确编译、链接、执行的。编译信息如下所示:
1. 1>------ 已启动生成: 项目: MainCpp, 配置: Debug Win32 ------
2. 1>main.cpp
3. 1>MainCpp.vcxproj -> G:\MainCpp\Debug\MainCpp.exe
========== 生成: 成功1 个,失败 0 个,最新 0 个,跳过 0 个==========
但是当你将代码放在Linux环境下,采用GCC编译器进行编译时,你会吃惊地发现编译器抛出了如下错误信息:
1.[develop@localhost ~]g++ main.cpp
main.cpp:2: 错误 :′::main′必须返回′int′
为什么同样的代码会出现两种不同的结果呢?这还是跨平台的C/C++语言吗?不要对C/C++的跨平台性产生质疑,之所以会这样,在很大程度上要归咎于市面上一些书的“误导”,以及微软对VC++编译器main返回值问题的纵容。 在C和C++中,不接收任何参数也不返回任何信息的函数原型为“void f(void);”,所以很多人认为,不需要程序返回值时可以把main函数定义成void main(void),然而这种想法是非常错误的。有一点必须明确:C/C++标准从来没有定义过void main()这样的代码形式。C++之父 Bjarne Stroustrup 在他的主页FAQ 中明确地写着这样一句话:“在C++中绝对没有出现过void main(){/* ... */}这样的函数定义,在C语言中也是。”
main函数的返回值应该定义为int类型,在C和C++标准中都是这样规定的。在C99标准中规定,只有以下两种定义方式是正确的:
1.int main( void )
2. int main( int argc, char *argv[] )
在C++03中也给出了如下两种main函数的定义方式:
1.int main()
2.int main( int argc, char *argv[] )
虽然C和C++标准并不支持void main(),但在部分编译器中void main()依旧是可以通过编译并执行的,比如微软的VC++。由于微软产品的市场占有率与影响力很大,因此在某种程度上加剧了这种不良习惯的蔓延。不过,并非所有的编译器都支持void main(),gcc就站在了VC++的对立面,它是这一不良习气的坚定抵制者,它会在编译时就明确地给出一个错误。
如果你坚持在某些编译器中使用void main()这种非标准形式的代码,那么当你把程序从一个编译器移植到另一个编译器时,你就要对可能出现的错误负责。除了有void main()这样的不规范格式外,在C语言程序中,尤其是一些老版本的C代码中,你还会经常看到main()这样的代码形式。
一些老的C标准(诸如C90)是支持main()这样的形式的。之所以支持,是因为在第一版的C语言中只有int一种数据类型,并不存在char、long、float、double等这些内置数据类型。既然只有int一种类型,也就不必显式地为main函数标明返回类型了。在Brian W.Kernighan和Dennis M.Ritchie的经典巨著The C Programming Language,Second Edition中用的就是main()。后来,在C语言的改进版中数据类型得到了扩充,为了能兼容以前的代码,标准委员会就做出了如下规定:不明确标明返回值的,默认返回值为int。在C99标准中,则要求编译器对于main()这种用法至少要抛出一个警告。
main函数返回值的作用,可以采用下面的方法加以验证。
首先,编写main.cpp文件,文件内容如下所示:
1.int main()
2.{
3. return 0;
4.}
在Linux环境下,采用命令:g++ main.cpp
生成可执行文件a.out。然后,执行命令:./a.out && ehco “success”
结果输出success。
修改上述程序:
1.int main( )
2.{
3.return -1;
4.}
做同样测试,无输出。
命令A && B中的&&类似于C++中的并操作(&&),如果A命令正确执行,接着就会执行命令B;如果A出现异常,则B不执行。通过以上分析可知,当main()返回0时,a.out正确执行并返回;但是如果返回-1,程序就不能正常返回了。
最后,还要说明C++标准中一个“好坏难定”的规定:在main函数中,return语句的作用在于离开main函数(析构掉所有具有动态生存时间的对象),并将其返回值作为参数调用exit函数。如果函数执行到结尾而没有遇到return语句,则其效果等同于执行了return 0。
也就是说,如果函数执行到main结束处时没有遇到return语句,编译器就会隐式地为你加上return 0;,效果与返回0相同。之所以说这条规定“好坏难定”,一方面是因为它让你省去了多敲几个字的麻烦,另一方面是因为这种便捷会让某些程序员忽视编译器代替他做的工作,而在思维中形成一种错误的认识:此函数可以无返回。
在应用这一规则时,需要注意以下两点:
(1)main函数的返回类型是int,不是void或其他类型。
(2)该规则仅仅对main函数适用。
按照以上标准得到了一个完全合乎C/C++标准的最小化的完整C++程int main(){} 要想保证程序具有良好的可移植性能,就要标明main函数返回int,而不是void。