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

14 浏览
0 Comments

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

在C语言中,主函数的有效签名有哪些?我知道的是:\n

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

\n还有其他有效的签名吗?

0
0 Comments

在POSIX系统上,execve()函数支持int main(int argc, char *argv[], char *envp[])这种形式的main()函数签名,其中新增的参数是环境变量,即一个由形如NAME=VALUE的字符串组成的数组。

然而,这种说法并不完全准确。实际上,execve()函数的环境变量参数与main()函数的调用约定无关,而是用于初始化extern char **environ;

事实上,在实践中,许多基于POSIX系统的C实现确实将第三个envp参数传递给main()函数。我不确定POSIX本身是否将这种形式作为main()函数的第三种有效签名进行了规定。你可以使用以下程序在GNU C上验证其实际工作情况:godbolt.org/z/9lie95(它将其argvenvp传递给execve("/usr/bin/env"),因此你可以看到它继承了一个合理的环境而不是返回-EFAULT)。但是,是的,这个回答描述得不正确,错误地暗示了execve的存在意味着main()的新签名。

根据上述内容,可以得出问题的原因是关于main()函数的有效签名的描述不准确,解决方法是对问题进行修正并提供正确的信息。

0
0 Comments

C语言的main()函数的有效签名有哪些?这个问题的出现主要是因为不同的标准和编译器对于main()函数的签名有不同的要求。根据C99、C11和C18标准,在主机环境下,main()函数可以有两种有效的签名:int main(void)和int main(int argc, char *argv[])。这两种签名可以互换使用,例如int可以替换为typedef定义的int类型,argv的类型可以写成char **argv等。而C++98标准则要求main()函数的返回类型必须为int,但是其他类型可以是实现定义的,同时也要求和C标准一样有两种签名:int main()和int main(int argc, char *argv[])。C++03、C++11、C++14和C++17标准基本上和C++98标准相同。

此外,Unix系统还支持第三种main()函数的签名:int main(int argc, char **argv, char **envp)。这种签名允许在main()函数中获取环境变量的信息,通过envp参数可以访问环境变量。C标准将这种签名视为一种常见的扩展,并在附录J中对其进行了说明。

对于Microsoft C编译器,存在一些特殊情况。根据Microsoft VS 2010编译器的官方文档,main()函数的声明语法可以是int main()或int main(int argc, char *argv[], char *envp[]),也可以声明为返回void类型,但是返回void类型的main()函数无法使用return语句返回退出码,必须使用exit()函数。需要注意的是,Microsoft的文档并没有提到C和C++标准规定的只有两个参数的main()函数签名。

最后,关于int main()和int main(void)是否相同的问题,从C++的角度来看,这两种写法是等价的,没有区别。而从C的角度来看,这两种写法有细微的差别,主要体现在递归调用main()函数的情况下。在C中,int main()不提供main()函数的原型,但只有在递归调用main()函数时才会注意到这一点。而int main(void)明确表示函数不接受任何参数,如果尝试提供参数调用该函数,将会导致编译错误。

总结起来,C语言的main()函数的有效签名取决于不同的标准和编译器,常见的有效签名有int main(void)和int main(int argc, char *argv[]),而C++语言的main()函数的有效签名要求返回类型为int,但其他类型可以是实现定义的。此外,Unix系统还支持第三种签名int main(int argc, char **argv, char **envp),而Microsoft C编译器有一些自己的规定。对于int main()和int main(void)的区别,C++中没有区别,而C中只有在递归调用main()函数的情况下才会有细微的差别。

0
0 Comments

C的main()函数的有效签名有哪些?

C11标准明确提到了以下两个签名:

int main(void);

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

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

相关的文本(第5.1.2.2.1节,但这个特定方面与C99没有变化)规定:

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

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

或者带有两个参数(这里称为argc和argv,尽管可以使用任何名称,因为它们是在声明它们的函数中局部的):

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

或者以某种其他实现定义的方式。

如果声明了它们,main函数的参数必须符合以下约束:

  • argc的值必须是非负的。

  • argv[argc]必须是一个空指针。

  • 如果argc的值大于零,则数组成员argv[0]到argv[argc-1](包括)必须包含指向字符串的指针,这些指针在程序启动之前由主机环境赋予给它们的实现定义的值。其目的是为程序提供在程序启动之前从托管环境中确定的信息。如果主机环境不能提供同时具有大写和小写字母的字符串,则实现必须确保以小写形式接收字符串。

  • 如果argc的值大于零,则argv[0]指向的字符串表示程序名称;如果程序名称不可从主机环境获取,则argv[0][0]必须是空字符。如果argc的值大于1,则argv[1]到argv[argc-1]指向的字符串表示程序参数。

  • 参数argc和argv以及argv数组指向的字符串必须可以被程序修改,并在程序启动和程序终止之间保留它们的最后存储值。

    请注意,这适用于托管环境,即C程序中通常见到的环境。无操作系统的自由环境(如嵌入式系统)的约束要少得多,如同标准的5.1.2.1节所述:

    在无操作系统的自由环境中(其中C程序的执行可能不受任何操作系统的任何好处),程序启动时调用的函数的名称和类型是实现定义的。除了第4条所要求的最小集合外,任何可供自由程序使用的库设施都是实现定义的。

    那么int main(int argc, const char* argv[]);呢?

    根据标准第5.1.2.2.1节的规定:“参数argc和argv以及argv数组指向的字符串必须可以被程序修改……”。因此,似乎在签名中使用const是无效的。

    为了使您的答案更具未来性,请提到“当前标准”是什么。

    我在问题中已经提到了这一点,提到了C11中的文本并指出C99几乎相同。但是我会根据您的建议,在第一段中再次重申。谢谢。

    有人可能会辩解int main(int argc, char* const argv[])是有效的,即将参数本身声明为const。我认为这是可以辩解的,因为它与假定的隐式声明int main(int argc, char* argv[]);是兼容的。

    具体而言,文本“argc和argv以及argv数组指向的字符串必须可以被程序修改”可能适用,尽管它可以被解读为“只有argv和argv[N]指向的字符,而不是组成argv的个别指针”。这就是为什么在标准中不应该使用不规范的语言的原因。

    我认为我们都同意argv(正确地)是可修改的(因为它是像所有函数参数一样的局部副本)-但是函数“实现”可以声明自己不修改这个局部变量,通过将其声明为const,而不改变函数签名。标准的意思是argv不指向const内存,argv中的指针也不指向const内存(也就是说我们可以说++argv是理所当然的,但标准要求我们也可以说++*argv,甚至++**argv(如果argc > 0)。

  • 0