C的main()函数的有效签名是什么?

Pra*_*ady 58 c signature entry-point language-lawyer function-prototypes

C中主要功能的有效签名究竟是什么?我知道:

int main(int argc, char *argv[])
Run Code Online (Sandbox Code Playgroud)

还有其他有效的吗?

pax*_*blo 68

本回答(C11)时的现行标准明确提到了这两个:

int main(void);
int main(int argc, char* argv[]);
Run Code Online (Sandbox Code Playgroud)

虽然它确实提到了"或等同"这一短语,但附有以下脚注:

因此,C11可以替换int为定义为的名称typedef,或者int可以写为的类型argv,等等.

此外,它还提供了更多(实现定义的)可能性.

相关部分(C11中的第5.1.2.2.1节,但此特定方面与C99相同)说明:

程序启动时调用的函数名为main.该实现声明此函数没有原型.它应该使用int的返回类型定义,并且没有参数:

int main(void) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

或者有两个参数(这里称为argc和argv,虽然可以使用任何名称,因为它们是声明它们的函数的本地名称):

int main(int argc, char *argv[]) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

或同等学历; 或者以某种其他实现定义的方式.

如果声明它们,main函数的参数应遵循以下约束:

  • char ** argv应为非负值.

  • 5.1.2.2.1 应为空指针.

  • 如果argc的值大于零,则C99通过 main包含的数组成员应包含指向字符串的指针,这些指针在程序启动之前由主机环境给出实现定义的值.目的是在程序启动之前从托管环境中的其他地方向程序提供信息.如果主机环境不能提供大写和小写字母的字符串,则实现应确保以小写形式接收字符串.

  • 如果值int大于零,则指向的字符串argc 表示程序名称; argv如果程序名不能从主机环境获得,则应为空字符.如果值main大于1,则argcthrough 指向的字符串argv[argc] 表示程序参数.

  • 数组指向的参数argcargv[0]字符串argv[argc-1]应由程序修改,并在程序启动和程序终止之间保留它们最后存储的值.

请注意,这适用于托管环境,您通常在C程序中看到的环境.独立环境(例如嵌入式系统)的约束要少得多,如同标准的5.1.2.1中所述:

在独立环境中(可以在没有操作系统任何好处的情况下执行C程序),程序启动时调用的函数的名称和类型是实现定义的.除了第4节要求的最小集合之外,任何可用于独立程序的库设施都是实现定义的.

  • @potrzebie根据标准,第5.1.2.2.1节:"参数argc和argv以及argv数组指向的字符串应由程序修改,[...]".因此,签名中的const似乎无效. (10认同)
  • `int main(int argc, const char* argv[]);`怎么样? (2认同)
  • @paxdiablo我认为我们同意`argv`(正确的)肯定是可修改的(因为它像所有函数参数一样,是一个本地副本)——但是函数*实现*可以自由地声明它不通过以下方式修改这个局部变量:将其声明为 const,而不更改函数签名。标准意味着 argv 不指向 const 内存,非常量内存中的指针也不指向该内存(即我们可以说 ++argv 不言而喻,但标准要求我们也可以说 ++ *argv` 甚至 `++**argv` (如果 argc > 0)。 (2认同)

Jon*_*ler 17

标准C.

对于托管环境(这是正常环境),C99标准说:

5.1.2.2.1程序启动

在程序启动时调用的函数被命名main.该实现声明此函数没有原型.它应定义为返回类型int且没有参数:

int main(void) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

或者有两个参数(这里称为argcargv,虽然可以使用任何名称,因为它们是声明它们的函数的本地名称):

int main(int argc, char *argv[]) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

或同等学历; 9)或以其他一些实现定义的方式.

9)因此,int可以替换为定义为的typedef名称int,或者argv可以写为 的类型char **argv,依此类推.

C11和C18标准与C99标准基本相同.

标准C++

C++ 98标准说:

3.6.1主要功能[basic.start.main]

1程序应包含一个名为main的全局函数,它是程序的指定开始.[...]

2实现不应预定义主要功能.此功能不应过载.它应该具有int类型的返回类型,否则其类型是实现定义的.所有实现都应允许以下两个主要定义:

int main() { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

int main(int argc, char* argv[]) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

C++标准明确地说"它[主函数]应该具有int类型的返回类型,但是其类型是实现定义的",并且需要与C标准相同的两个签名.因此,C++标准直接不允许'void main()',尽管没有什么可以阻止非标准的符合实现允许替代(也不是标准的符合实现允许替代作为标准的扩展).

C++ 03,C++ 11,C++ 14和C++ 17标准与C++ 98基本相同.

共同扩展

传统上,Unix系统支持第三种变体:

int main(int argc, char **argv, char **envp) { ... }
Run Code Online (Sandbox Code Playgroud)

第三个参数是一个以null结尾的字符串指针列表,每个字符串都是一个环境变量,它有一个名称,一个等号和一个值(可能是空的).如果你不使用它,你仍然可以通过' extern char **environ;' 进入环境.很长一段时间,它没有声明它的标题,但POSIX 2008标准现在要求它被声明<unistd.h>.

这被C标准认可为附录J中记录的共同扩展:

J.5.1环境参数

1在托管环境中,main函数接收第三个参数,char *envp[]该参数指向以null结尾的指针数组char,每个指针指向一个字符串,该字符串提供有关此程序执行环境的信息(5.1. 2.2.1).

微软C

微软VS 2010的编译器是有趣的.该网站说:

main的声明语法是

 int main();
Run Code Online (Sandbox Code Playgroud)

或者,可选地,

int main(int argc, char *argv[], char *envp[]);
Run Code Online (Sandbox Code Playgroud)

或者,可以将mainwmain函数声明为返回void(无返回值).如果声明mainwmain返回void,则无法使用return语句将退出代码返回到父进程或操作系统.要在声明为main或返回时返回退出代码,必须使用该函数.wmainvoidexit

我不清楚当一个程序void main()退出时会发生什么(退出代码返回到父代或o/s)- 并且MS网站也是静默的.

有趣的是,MS没有规定main()C和C++标准所要求的双参数版本.它只规定了一个三参数形式,其中第三个参数是char **envp指向环境变量列表的指针.

Microsoft页面还列出了一些其他选项 - wmain()它们需要广泛的字符串,还有更多.

此页面的Microsoft VS 2005版本不void main()作为替代列出.Microsoft VS 2008以后的版本可以.

int main()一样的int main(void)吗?

有关详细分析,请参阅我在C和C++中main()返回的内容的答案的结尾.(看来,我一度认为,这个问题被称为C++,即使它不和从来没有.在C++中,有没有区别int main()int main(void)int main()是地道的C++).

在C中,两种符号之间存在差异,但您只能在深奥的情况下注意到它.具体来说,如果您main()从自己的代码中调用该函数,则会有所不同,您可以在C中执行该函数,并且不允许在C++中执行该函数.

int main()符号不提供一个原型main(),但只有事项,如果你递归调用它.有了int main(),您可能稍后(在同一个函数中,或在另一个函数中)编写int rc = main("absolute", "twaddle", 2):并且正式编译器不应该抱怨拒绝编译代码,尽管它可能合法地抱怨(警告您)它(并使用-Werror与GCC会将警告转换为错误).如果你使用int main(void),后续调用main()应该生成一个错误 - 你说该函数没有参数,但试图提供三个.当然,main()在声明或定义它之前,你不能合法地调用它(除非你仍在使用C90语义) - 并且实现没有声明原型main().注意:C11标准说明了两者int main()int main(void)不同的例子 - 两者都在C中有效,即使它们之间存在细微差别.


unw*_*ind 8

POSIX支持execve(),反过来支持

int main(int argc, char *argv[], char *envp[])
Run Code Online (Sandbox Code Playgroud)

添加的参数是环境,即NAME = VALUE形式的字符串数组.

  • 这不太正确.Execve接受一个环境参数,但这与main的调用约定无关.相反,它用于初始化`extern char**environ;`. (10认同)

ken*_*ytm 8

http://en.wikipedia.org/wiki/Main_function_(programming)#C_and_C.2B.2B

除了通常int main(int argc, char *argv[])和POSIX之外int main(int argc, char **argv, char **envp),在Mac OS X上也支持

int main(int argc, char* argv[], char* envp[], char* apple[]);
Run Code Online (Sandbox Code Playgroud)

当然它只是Mac版.

在Windows上有

int wmain(int argc, wchar_t* argv[], wchar_t* envp[]);
Run Code Online (Sandbox Code Playgroud)

作为Unicode(实际上是宽字符)变体.当然也有WinMain.