关于 void main 和 int main

  main 函数的返回值大家注意了吗?也许有人会说可以没有返回值,如果你深入到程序的 CRT启动代码,你会发现....

1 __initenv = envp;
2 mainret = main(argc, argv, envp);
3 if ( !managedapp )
4 exit(mainret);
5 if (has_cctor == 0)
6 cexit();

  按照新的 C99 标准,即使函数本身没有定义返回值,编译器也会加上,以返回给激发程序运行状态。很多人甚至市面上的一些书籍,都使用了 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 和 int main

  很多C程序员都曾搞错的一个概念,就是以为这样一个函数不接受任何参数:int foo();事实上,这个函数被认为可以接受未知个数的参数(译:可接受任意多的参数!)。正确的用法是在括号内添加关键字void。
  在 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。总而言之:void main 主函数没有返回值,main 默认为 int 型,即 int main(), 返回整数。注意,新标准不允许使用默认返回值,即 int 不能省,而且对应 main 函数不再支持 void 型返回值,因此为了使程序有很好的移植性,强烈建议使用:
int main()
{
  return 0;   /* 新标准主函数的返回值这条语句可以省略 */
}
  返回值的作用: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。

  最后,关于为什么void main(void)是一种错误的用法,这里附上一篇更细节化的英文文章:

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.

时间: 2024-10-18 09:30:15

关于 void main 和 int main的相关文章

void main(), int main() 和int main(void)的区别

1.区别是main()函数是否有返回值.2.void定义的函数没有返回值,int定义的函数返回整型值.3.void,字面意思是"无类型",常用在程序编写中对定义函数的参数类型.返回值.函数中指针类型进行声明,有注释和限制程序的作用. 4.标准的main函数格式为:int main(int argc, char *argv[]);即返回值为整型,带两个参数,argc为命令行参数的个数,argv为指针数组,前argc个指针为参数列表,最后一个指针值为NULL.

void main() &amp;&amp; int main()

不存在void main(),只有int main() void main 主函数没有返回值,main 默认为int 型,即 int main(), 返回整数.注意,新标准不允许使用默认返回值,即int不能省,而且对应main函数不再支持void型返回值,因此为了使程序有很好的移植性,强烈建议使用:     int main()     {          return 0; /* 新标准主函数的返回值这条语句可以省略 */       }      返回值的作用:     main函数的返回

int main()还是void main()

按照新的C99标准,即使函数本身没有定义返回值,编译器也会加上,以返回给激发程序,运行状态.很多人甚至市面上的一些书籍,都使用了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 be

int main()和void main()的区别

int main()括号里面为空代表不确定参数,并不是说不带参数.如果要告诉编译器函数不带参数,做法是:int main(void)int main()需要返回一个整形值,也就是我们经常会看到在这个函数末尾会加上return 0 void main()代表这个函数不需要返回任何值,比如:void printStar(){printf("**********\n");}

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

argc是命令行总的参数个数     argv[]是argc个参数,其中第0个参数是程序的全名,以后的参数     命令行后面跟的用户输入的参数,比如:     int   main(int   argc,   char*   argv[])     {     int   i;     for   (i   =   0;   i<argc;   i++)     cout<<argv[i]<<endl;     cin>>i;     return   0;  

38&gt;&gt;int main( int argc, char **argv)

1.参数 (有时参数是void) argc是程序运行时参数个数 argv是存储参数的数组,可以用char* argv[],也可以用char **argv. 例如编译一个hello.c的程序 1 #include<stdio.h>  2 int main(int argc,char *argv[])  3 {  4     printf("%d\n",argc);  5     printf("%s\n",argv[0]);  6     /*printf

int main(int argc,char *argv[])的具体含义

int main(int argc,char * argv[]) argv为指针的指针 argc为整数 char **argv or: char *argv[] or: char argv[][] main()括号内是固定的写法. 下面给出一个例子来理解这两个参数的用法: 假设程序的名称为prog, 当只输入prog,则由操作系统传来的参数为: argc=1,表示只有一程序名称. argc只有一个元素,argv[0]指向输入的程序路径及名称:./prog 当输入prog para_1,有一个参数

关于int main(int argc,char* argv[])详解

平时在VS的环境下,主函数总会看到这两个参数,今天突然很想知道这两个参数的原理以及作用,因此查了下资料.真心受教了. 下面的博文是在百度空间看一位大神的,原文链接:http://hi.baidu.com/sgglong70626/item/8881322b2dce21c1ee10f11e argc是命令行总的参数个数   argv[]为保存命令行参数的字符串指针,其中第0个参数是程序的全名,以后的参数为命令行后面跟的用户输入的参数,argv参数是字符串指针数组,其各元素值为命令行中各字符串(参数

int main (int argc, const char * argv[0]) 中参数的含义;指针数组和数组指针

恩,有的编译器初始化时候会产生这样的参数 argc是命令行总的参数个数,argv[]是argc个参数,其中第0个参数是程序的全名 1. 几种C++ 常见的参数种类 int main(void); int main(); int main(int argc, char **argv);   //等价于int main(int argc, char *argv[]),是否等价呢?是不是前一个可以表示任意长度的任意个数组,后一个只是定长的任意个数的数组?见下面 int main(int argc, c