学校首页  教务选课  本站首页  计算中心简介  部门工作  对外交流  C语言学习  大学计算机  PAT考试  咨询与投诉 
 首页 > 常见问题 > 正文 更多常见问题
C/C++ 误区一:void main()

2011年05月19日19:33  来源:计算中心  访问量:41115


  (转载,来源:网络,略有修改)

  很多人甚至市面上的一些书籍,都使用了void main( ),其实这是错误的。C/C++中从来没有定义过void main( )。C++之父Bjarne Stroustrup在他的主页上的FAQ中明确地写着The definition void main( ) { /* ... */ } is not and never has been C++, nor has it even been C.( void main( )从来就不存在于C++或者C)。下面我分别说一下C和C++标准中对main函数的定义。

  1. C

  在C89中,main( )是可以接受的。Brian W. Kernighan和Dennis M. Ritchie的经典巨著The C programming Language 2e(《C 程序设计语言第二版》)用的就是main( )。不过在最新的C99标准中,只有以下两种定义方式是正确的:

  int main(void)

  int main(int argc, char *argv[])

  (参考资料:ISO/IEC 9899:1999 (E) Programming languages ? C 5.1.2.2.1 Program startup)

  当然,我们也可以做一点小小的改动。例如:char *argv[]可以写成char **argv;argv和argc可以改成别的变量名(如intval和charval),不过一定要符合变量的命名规则。

  如果不需要从命令行中获取参数,请用int main(void);否则请用int main(int argc, char *argv[])。

  main函数的返回值类型必须是int,这样返回值才能传递给程序的调用者(如操作系统)。

  如果main函数的最后没有写return语句的话,C99规定编译器要自动在生成的目标文件中(如exe文件)加入return 0;,表示程序正常退出。不过,我还是建议你最好在main函数的最后加上return语句,虽然没有这个必要,但这是一个好的习惯。注意,vc6不会在目标文件中加入return 0;,大概是因为vc6是98年的产品,所以才不支持这个特性。现在明白我为什么建议你最好加上return语句了吧!不过,gcc3.2(Linux下的C编译器)会在生成的目标文件中加入return 0;。

  2. C++

  C++98中定义了如下两种main函数的定义方式:

  int main( )

  int main(int argc, char *argv[])

  (参考资料:ISO/IEC 14882(1998-9-01)Programming languages ? C++ 3.6 Start and termination)

  int main( )等同于C99中的int main(void);int main(int argc, char *argv[])的用法也和C99中定义的一样。同样,main函数的返回值类型也必须是int。如果main函数的末尾没写return语句,C++98规定编译器要自动在生成的目标文件中加入return 0;。同样,vc6也不支持这个特性,但是g++3.2(Linux下的C++编译器)支持。

  3. 关于void main

  在C和C++中,不接收任何参数也不返回任何信息的函数原型为“void foo(void);”。可能正是因为这个,所以很多人都误认为如果不需要程序返回值时可以把main函数定义成void main(void)。然而这是错误的!main函数的返回值应该定义为int类型,C和C++标准中都是这样规定的。虽然在一些编译器中,void main可以通过编译(如vc6),但并非所有编译器都支持void main,因为标准中从来没有定义过void main。g++3.2中如果main函数的返回值不是int类型,就根本通不过编译。而gcc3.2则会发出警告。所以,如果你想你的程序拥有很好的可移植性,请一定要用int main。

  不要用“我的老师告诉我这么做是对的”之类的话来为自己开脱;老师们总是习惯犯错误(teachers have a bad habit of being wrong)。写安全的,合乎标准的代码,大家就可以专注于你程序中其它的问题而不是在这种规范方面的东西上浪费时间。

  应当指出:在某些系统中,若程序使用void main定义或没有return值,则可能导致堆栈异常从而导致系统故障。(详见后面英文部分)

  4. 返回值的作用

  main函数的返回值用于说明程序的退出状态。如果返回0,则代表程序正常退出;返回其它数字的含义则由系统决定。通常,返回非零代表程序异常退出。下面我们在winxp环境下做一个小实验。首先编译下面的程序:

  int main(void)

  {

  return 0;

  }

  然后打开附件里的“命令提示符”,在命令行里运行刚才编译好的可执行文件,然后输入“echo %ERRORLEVEL%”,回车,就可以看到程序的返回值为0。假设刚才编译好的文件是a.exe,如果输入“a && dir”,则会列出当前目录下的文件夹和文件。但是如果改成“return -1”,或者别的非0值,重新编译后输入“a && dir”,则dir不会执行。因为&&的含义是:如果&&前面的程序正常退出,则继续执行&&后面的程序,否则不执行。也就是说,利用程序的返回值,我们可以控制要不要执行下一个程序。这就是int main的好处。如果你有兴趣,也可以把main函数的返回值类型改成非int类型(如float),重新编译后执行“a && dir”,看看会出现什么情况,想想为什么会出现那样的情况。顺便提一下,如果输入a || dir的话,则表示如果a异常退出,则执行dir。

  5. 那么int main(int argc, char *argv[], char *envp[])呢?

  这当然也不是标准C/C++里面定义的东西!char *envp[]是某些编译器提供的扩展功能,用于获取系统的环境变量。因为不是标准,所以并非所有编译器都支持,故而移植性差,不推荐使用——除非你的程序是专门设计用于工作在特定的环境中而且需要获取系统的环境变量。

--------------------------------------------------
来源:http://users.aber.ac.uk/auj/voidmain.shtml

  void main(void) - the Wrong Thing

  The newsgroup, comp.lang.c, is plagued by an almost continuous discussion of whether we can or cannot use void as a return type for main. The ANSI standard says "no", which should be an end of it. However, a number of beginners' books on C have used void main(void) in all of their examples, leading to a huge number of people who don't know any better.

  When people ask why using a void is wrong, (since it seems to work), the answer is usually one of the following:

  Because the standard says so.
  (To which the answer is usually of the form "but it works for me!")

  Because the startup routines that call main could be assuming that the return value will be pushed onto the stack. If main() does not do this, then this could lead to stack corruption in the program's exit sequence, and cause it to crash.
  (To which the answer is usually of the form "but it works for me!")

  Because you are likely to return a random value to the invokation environment. This is bad, because if someone wants to check whether your program failed, or to call your program from a makefile, then they won't be able to guarantee that a non-zero return code implies failure.
  (To which the answer is usually of the form "that's their problem").

  This page demonstrates a system on which a void main(void) program will very likely cause problems in the third class above. Calling the program from a script may cause the script to die, whether or not its return code is checked. Calling it from a makefile may cause make to complain. Calling it from the command line may cause an error to be reported.

  RISC OS is the native operating system of Acorn's range of ARM based computers. One of the facilities of this OS is a system variable, Sys$RCLimit. The value of this variable specifies the maximum value that a program may return to the OS without causing RISC OS itself to raise an error. The default value of this variable is set by the OS at 256. I'm not too sure what the intended function of this variable was, but it exists, and that's that.

  Now, let's look at an example program using int main(void).

  int main(void)
  {
          return 42;
  }

  Compiling it to ARM assembly language, using gcc (as an aside: Acorn's own C compiler reports a warning with void main(void) and converts it to an integer function returning zero) gives the following:

  |main|:
          mov     ip, sp
          stmfd   sp!, {rfp, fp, ip, lr, pc}
          sub     fp, ip, #4
          cmps    sp,sl
          bllt    |x$stack_overflow|
          bl      |___main|

          mov     r0, #42
          ldmdb   fp, {rfp, fp, sp, pc}^

  The first six instructions are initialisation and stack checking. The final two return 42 to the library startup code. So, the return value of main is passed in R0. Note that the library startup code is expecting to call a function returning an integer, so will happily use the value returned in R0.

  What happens with a void main function? Well, here's an example.

  #include <stdio.h>

  char buf[1024];

  void main(void)
  {
          (void)fgets(buf, 1024, stdin);
  }

  The program waits for a line of text from its standard input, nothing else. Again we compile it to assembler:

  |.LC0|:
          dcd     |__iob|
  |.LC1|:
          dcd     |buf|
  |main|:
          mov     ip, sp
          stmfd   sp!, {rfp, fp, ip, lr, pc}
          sub     fp, ip, #4
          cmps    sp,sl
          bllt    |x$stack_overflow|
          bl      |___main|

          ldr     r2, [pc, #|.LC0| - . - 8]
          mov     r1, #1024
          ldr     r0, [pc, #|.LC1| - . - 8]

          bl      |fgets|

          ldmdb   fp, {rfp, fp, sp, pc}^

          area    |buf|, DATA, COMMON, NOINIT
          %       1024

  Again, the first six instructions in main set things up. The next three set up the arguments for the call to fgets. Then we call fgets and return to the caller. stdio.h says that fgets returns a pointer to the buffer. So, in this instance, what we are returning to the library startup code is a pointer to buf. Under RISC OS, all C programs are mapped into memory at 0x8000. So, we will be returning a value to the OS which is > 32768 (hence, certainly > the default value of Sys$RCLimit). The OS then raises an error.

  Here's the result of compiling and running the program:

  SCSI: void % gcc void.c -o void
  Drlink AOF Linker  Version 0.28  30/07/95
  SCSI: void % show Sys$RCLimit
  Sys$RCLimit : 256
  SCSI: void % void
  I enter this line
  Return code too large
  SCSI: void %

  And, in a script file:

  SCSI: void % cat script

  void
  echo Finished

  SCSI: void % run script
  I enter this line
  Return code too large
  SCSI: void %

  The error interrupts the script before the second command is run.

  Note that the example above was a little contrived in order to make the final function call return a pointer. A better example where this could cause problems is one where the program uses printf to report a usage string > 256 characters long prior to returning or, worse still, one where the program uses printf to output data depending on user input. Depending on the length of the user's input text, the program may or may not cause an error which is solely due to the use of void as a return type for main.

  So, if you want your software to be portable, please make main return int. It does matter.


 前一条:C程序:背和理解间的关系 (2011-04-16)
 后一条:无
相关报道
·C语言程序设计上机练习技巧、学习方法与注意事项 (2011-04-20)
 
 
 
 
 

 

◆ MOOC+SPOC

C语言程序设计MOOC
Office高级应用MOOC
大学计算机MOOC(56学时)
大学计算机MOOC(32学时)

◆ 英语工具(*=机房开放)

离线词典:有道词典(绿色版)
离线翻译:金山快译(绿色版)
* 谷歌翻译
* 百度词典/翻译/论文写作助手
* 金山词霸/翻译/写作批改
必应词典/翻译
有道词典/翻译/简历助手

◆ 重要公告

2024 年(第17届)中国大学…
2023-2024学年第2学期《Offi…
第三届“成都信息工程大学团…
20232学期非计算机类《C语言…
第二届成都信息工程大学新生…
第十五届蓝桥杯全国软件和信…
20231学期非计算机类《C语言…
20222学期非计算机类《C语言…
2022-2023第2学期《Office高…
20221学期非计算机类《C语言…

◆ 校园热线

教学平台 数字图书 招生就业
学生事务 心灵之约 邮件服务
后勤管理
四川·成都市西南航空港经济开发区学府路一段24号  邮编:610225
学校值班电话:028-85966502     联系计算中心:电话028-85966005  电子邮件  留言
Copyright©成都信息工程大学计算中心 2008-2011  建议使用IE5.5,1024*768以上浏览