main原型考证及程序终止行为

在C和C++中流传着很多版本的main函数原型,不同的书里也有不同的写法。今天我从几种标准(C89/99/11以及C++98/03/11/14)的角度来寻找一下什么是“标准行为”以及在主函数中return后发生了什么。

比较常见的是下面几种:

1
2
3
4
5
void main()
main()
int main()
int main(void)
int main(int argc,char *argv[])

void main()

首先,从标准角度(所有版本)来说,void main()肯定是错的,没有任何标准(C89/99/11以及C++98/03/11/14)中允许过这种写法。

但是我在APUE里看到了一种把主函数写为void main()的原因,不知道是不是有人从这个角度说的然后就以讹传讹了。

The problem is that these compilers don’t know that an exit from main is the same as a return. One way around these warnings, which become annoying after a while, is to use return instead of exit from main. But doing this prevents us from using the UNIX System’s grep utility to locate all calls to exit from a program. Another solution is to declare main as returning void, instead of int, and continue calling exit. This gets rid of the compiler warning but doesn’t look right (especially in a programming text), and can generate other compiler warnings, since the return type of main is supposed to be a signed integer.

还有一种可能是从嵌入式来的,没有操作系统,入口点是硬件实现,返回任何东西都没意义。来源于知乎用户@James Swineson的评论。

main()

在K&R C与C89里,函数没有显式声明返回类型,则默认是int:

C89对函数定义的语法(Syntax)描述如下(注意declaration-specifiersopt下标符号):

$${declaration\textrm{-}specifiers_{opt}}\hspace{2mm}{declarator\hspace{2mm}declaration\textrm{-}list_{opt}}\hspace{2mm}{compound\textrm{-}statement}$$

C89中declaration-specifiersSyntax上为:

  • storage-class-specifier
  • type-specifier
  • type-qualifier

表明在C89中函数的return type可以省略。

K&R C里的描述如下:

Various parts may be absent; a minimal function is

1
dummy() {}

which does nothing and returns nothing. A do-nothing function like this is sometimes useful as a place holder during program development. If the return type is omitted, int is assumed.

所以说:

1
2
3
func(){}
// 等价于
int func(){}

但是这种方式在C99之后就被废除掉了(注意declaration-specifiers没有opt下标了):

$$declaration\textrm{-}specifiers\hspace{2mm}declarator\hspace{2mm}declaration\textrm{-}list_{opt}\hspace{2mm}compound\textrm{-}statement​$$

综上,在C89中,函数的返回类型可以省略,但默认为int,即

主函数声明main()隐式是int main()

int main()

int main()int main(void)在C语言中是有区别的:

1
2
3
int main()
// 不等价于
int main(void)

在C语言中参数列表为空(即不提供参数列表也不为void),表示不提供参数数量和参数类型信息:

1
2
3
4
5
6
7
8
int func(){
print("func()\n");
return 0;
}

int main(void){
func(1,2,3,4);// call func();
}

The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of theparameters is supplied.

C99/11 Standard

在C99/11标准中,明确定义了对于标准的main函数的两个原型:

The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters:

1
int main(void) { /* ... */ }

or with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared):

1
int main(int argc, char *argv[]) { /* ... */ }

or equivalent;or in some other implementation-defined manner.
If they are declared, the parameters to the main function shall obey the following constraints:

  • The value of argc shall be nonnegative.
  • argv[argc] shall be a null pointer.
  • If the value of argc is greater than zero, the array members argv[0] through argv[argc-1] inclusive shall contain pointers to strings, which are given implementation-defined values by the host environment prior to program startup. The intent is to supply to the program information determined prior to program startup from elsewhere in the hosted environment. If the host environment is not capable of supplying strings with letters in both uppercase and lowercase, the implementation shall ensure that the strings are received in lowercase.
  • If the value of argc is greater than zero, the string pointed to by argv[0]
    represents the program name; argv[0][0] shall be the null character if the program name is not available from the host environment. If the value of argc is greater than one, the strings pointed to by argv[1] through argv[argc-1]
    represent the program parameters.
  • The parameters argc and argv and the strings pointed to by the argv array shall be modifiable by the program, and retain their last-stored values between program startup and program termination.

C++ Standard

由于C和C++中对于函数参数列表的规则并不一致(C++中参数列表为空代表不接收任何参数)。所以C++中main的原型和ISO C也并不太一样:

  • a function of () returning int and
  • a function of (int, pointer to pointer to char) returning int

main return value

main必须要有返回值的原因是:在C和C++中使用return-statement都是将return的值作为参数来调用exit/std::exit来终止程序。

If status is zero or EXIT_SUCCESS, an implementation-defined form of the status successful termination is returned.

ISO C99/11:
If the return type of the main function is a type compatible with int, a return from the initial call to the main function is equivalent to calling the exit function with the value returned by the main function as its argument;reaching the } that terminates the main function returns a value of 0. If the return type is not compatible with int, the termination status returned to the host environment is unspecified.

ISO C++11/14:
A return statement in main has the effect of leaving the main function (destroying any objects with automatic storage duration) and calling std::exit with the return value as the argument. If control reaches the end of main without encountering a return statement, the effect is that of executing

1
return 0;

exit

1
2
#include <stdlib.h>
void exit(int status);

The exit function causes normal program termination to occur. If more than one call to the exit function is executed by a program, the behavior is undefined.

  • First, all functions registered by the atexit function are called, in the reverse order of their registration,except that a function is called after any previously registered functions that had already been called at the time it was registered. If, during the call to any such function, a call to the longjmp function is made that would terminate the call to the registered function, the behavior is undefined.
  • Next, all open streams with unwritten buffered data are flushed, all open streams are closed, and all files created by the tmpfile function are removed.
  • Finally, control is returned to the host environment. If the value of status is zero or EXIT_SUCCESS, an implementation-defined form of the status successful termination is returned. If the value of status is EXIT_FAILURE, an implementation-defined form of the status unsuccessful termination is returned. Otherwise the status returned is implementation-defined.

The exit function cannot return to its caller.

_Exit

1
2
#include <stdlib.h>
void _Exit(int status);

The _Exit function causes normal program termination to occur and control to be returned to the host environment. No functions registered by the atexit function or signal handlers registered by the signal function are called. The status returned to the host environment is determined in the same way as for the exit function (7.20.4.3).Whether open streams with unwritten buffered data are flushed, open streams are closed,or temporary files are removed is implementation-defined.
The _Exit function cannot return to its caller.

全文完,若有不足之处请评论指正。

微信扫描二维码,关注我的公众号。

本文标题:main原型考证及程序终止行为
文章作者:查利鹏
发布时间:2017/02/27 15:30
本文字数:5.7k 字
原始链接:https://imzlp.com/posts/15272/
许可协议: CC BY-NC-SA 4.0
文章禁止全文转载,摘要转发请保留原文链接及作者信息,谢谢!
您的捐赠将鼓励我继续创作!