我正在阅读教程,我已经阅读那里,我们不能定义void main()
作为原型已经定义为int main() or int main(int argc , char *argv)
这些只是两个有效的方式来定义C中的主要功能。 所以在哪个头文件或库文件这些原型设置为 我不知道如何编译器给出错误float main()
后面的机制是什么,我的意思是它是一个语法错误或为main()定义的一些原型?c或C++中main()函数的原型?
请用简单的语言给出答案。
我正在阅读教程,我已经阅读那里,我们不能定义void main()
作为原型已经定义为int main() or int main(int argc , char *argv)
这些只是两个有效的方式来定义C中的主要功能。 所以在哪个头文件或库文件这些原型设置为 我不知道如何编译器给出错误float main()
后面的机制是什么,我的意思是它是一个语法错误或为main()定义的一些原型?c或C++中main()函数的原型?
请用简单的语言给出答案。
棒的标准形式,
int main(int argc, char* argv[])
或
int main(void)
,如果你从一个编译器的移动程序到另一个你不会遇到问题。
这就是说,ISO/IEC 9899:201X - > 5.1.2.2.1(程序启动)启动状态:
称为在程序启动的功能被命名为主力。 实现声明没有原型这个函数。它应 与的INT返回类型和不带参数进行定义:
int main(void) { /* ... */ }
或两个参数(这里称为argc和argv,虽然任何 名称可以使用,因为它们是地方于它们是 声明的功能):
int main(int argc, char *argv[]) { /* ... */ }
或等同物; 10)或某种其他实施方式定义的 方式。 。
。
。
10)因此,int可以被定义为int的 typedef名称替代,或者argv的类型可以被写为 char ** argv,依此类推。
问题中没有提到托管环境。您通过引用托管环境的子章节来错误地引用标准,从而忽略了标准中独立环境的整个章节。 – Lundin
我不认为有主函数的原型。这是您的程序的入口点,它由标准定义。
例如C标准定义是这样:
5.1.2.2.1计划启动
1名为在程序启动的功能被命名为主力。 实现声明此函数没有原型。应当带有int返回类型和不带参数来 定义:
int main(void){}
或具有两个参数(此处称为argc和argv,虽然任何名字>可以使用,因为它们是本地的在它们 声明功能):
int main(int argc, char *argv[]){}
或等同物; 9)或在一些其它实现定义的方式。
问题中没有提到托管环境。您通过引用托管环境的子章节来错误地引用标准,从而忽略了标准中独立环境的整个章节。 – Lundin
C11 Standard指示什么是有效的和无效的C(旧标准具有相同的规则)。
在对于main
签名它说(5.1.2.2.1):
称为在程序启动的功能被命名为主力。该实现没有声明这个函数的原型。它应为int的返回类型和不带参数进行定义:
int main(void) { /* ... */ }
或两个参数(这里称为argc和argv,虽然可以使用任何名字,因为他们是当地的在其中声明它们的功能):
int main(int argc, char *argv[]) { /* ... */ }
或同等或在一些其它实现定义的方式。
所以,除了一些其他实现定义的方式,唯一有效的签名都是这两个。 如果一个实现定义了一个不同的签名,您可以自由使用该签名,前提是您不介意丢失一些代码可移植性。
所以下面都是一个编译器无效无额外:
int main() { /* ... */ }
void main() { /* ... */ }
void main(void) { /* ... */ }
double main() { /* ... */ }
int main(int argc, double *argv[]) { /* ... */ }
int main(int argc, char **argv, char **envp) { /* ... */ }
你的教程是不完全错,但它描述了一个非常严格执行的行为,比C标准严格要求的实现成为。
C标准中关于程序启动时调用的函数的说法就是某些可能性需要工作。它并不是说需要其他东西而不是才能工作。另外,C标准对编译器错误的说法很少。现代C实现通常超出标准对诊断的要求;支持他们接受的一套程序的许多扩展也是很常见的。
在“托管”的环境,一个提供所有的标准C库,让启动名称和签名int main(void)
或int main(int argc, char **argv)
被符合呼吁程序的功能程序的设施。他们需要工作。但是这个标准允许这个函数被“以某种其他实现定义的方式”进行声明,并且存在许多替代名称和签名:我将列出一些最常见的名称和签名。
int main(int argc, char **argv, char **envp)
void main(void)
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
如果它支持这些替代入口点的名称或签名之一的执行文件,这是完全可以使用它。 (你的程序将不会是“严格符合”,但几乎没有真正的计划是“严格符合”,所以不要担心。)
在一个“独立”的环境,这不提供所有的标准C库中,在程序启动时调用的函数的名称和签名留给实现 - 您可能不得不使用诸如EFI_STATUS efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
之类的古怪东西。 但是,int main(void)
可以正常工作,而int main(int argc, char **argv)
可以正常工作(在运行时收到的参数可能是垃圾)。现在
,如果你有一个托管环境中会发生什么,你使用了一个没有记录工作的入口点函数的名称和/或签名? C标准说你的程序在这种情况下有未定义行为- 什么都可以发生。一些常见的事情将会发生:
编译器确实发出了一个错误或至少是一个警告。它没有从任何头文件获取正确的原型;相反,正确的原型据说是内置于的编译器,在编译器自己的源代码中定义。现在很多C库函数以及main
都是这种情况。演示:
$ cat > test.c <<\!
extern int exit(int); // wrong, `exit` should return `void`
void main(void) {} // wrong, `main` should return `int`
!
$ gcc -fsyntax-only -std=gnu11 -Wall test.c
test.c:1:12: warning: conflicting types for built-in function ‘exit’
test.c:2:6: warning: return type of ‘main’ is not ‘int’
(由于历史原因,GCC是几乎没有挑剔别人的代码,因为它可能是,很多的事情,从现代的眼光看,应该是错误仅仅是警告,并如果你从头开始编写新的代码并使用GCC,我建议基本上总是使用-std=gnu11 -Wall -Wextra -Wpedantic -Werror
选项,但不是-std=c11
,因为这会使关闭您可能需要的扩展名,并且可以也暴露系统头文件中的错误)。
prog内存无法链接。这是什么情况,举例来说,如果你试图弥补自己的名字,而不是调用它main
:
$ cat > test.c <<\!
extern int puts(const char *);
void my_program_starts_here(void) { puts("hello world"); }
!
$ gcc -std=gnu11 -Wall test.c
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o:
In function `_start':
(.text+0x20): undefined reference to `main'
这是更神秘的错误,你可以走出连接器中的一个,所以我将它解开一点。你有没有想过如何调用main
?这是令人惊讶的简单:有C库提供的功能,通常被称为_start
,其最后一行是一样的东西
exit(main(argc, argv, environ));
由于历史原因,该功能不与libc.so
大部分C库的捆绑。它位于一个单独的目标文件crt1.o
中,编译器在被要求链接一个程序时会自动引入(就像它自动在-lc
上一样)。因此,当您未定义main
时,从_start
对main
的引用不满足,并且链接失败。
(OK,如何_start
被调用这就是你进入更深的魔法问另外一个问题吗?)
最后,程序可以编译和链接罚款,甚至出现正常工作 - 但看起来更难,你发现它是行为不端。如果您在Unix系统上使用void main(void)
,会发生这种情况。 (一阶,除Windows以外的所有托管的环境是Unix系统现在。)
$ cat > test.c <<\!
extern int puts(const char *);
void main(void) { puts("hello world"); }
!
$ gcc -std=gnu11 test.c
$ ./a.out
hello world
没有-Wall
,而不是从编译器窥视,程序运行罚款......或者做的?
$ ./a.out ; echo $?
hello world
12
该数字应从main
返回的值成为该程序的exit status,其出现在壳可变$?
。如果main
已正确声明为返回int
,并且末尾有return 0;
,则echo $?
将打印0.从哪里来的?可能是puts
的返回值,编译器在从main
返回之前没有打扰从返回值寄存器中清除。
很容易没有注意到这个错误,但它是一个错误,并且第一个尝试编写涉及你的程序的shell脚本的人将会对你很恼火。
有关退出状态的一些注解,主要用于学究:
在C++中,并用C开始与1999年的标准,你在技术上允许省略任何明确return 0;
在main
只要你声明正确,但我认为依靠这是糟糕的风格。
在Unix上的很多,但并非所有的实现,在$?
中显示的值将只能从main
返回的值低了七八位。这是用于检索子进程退出状态的系统调用的一个限制,waitpid
。
甲严格符合 ISO C程序只能从main
返回三个值:0,EXIT_SUCCESS
,和EXIT_FAILURE
;后两个常量在stdlib.h
中声明。从main
返回零的效果保证是一样返回EXIT_SUCCESS
的效果,但不能保证的值相等。
在实践中,返回至少0,1和2以及其中EXIT_SUCCESS != 0
和/或EXIT_FAILURE != 1
早已走到天空中的一大桶的实现是安全的,所以不用担心它。
这里没有任何原型。这个限制是编译器本身的一部分。 – HolyBlackCat
“这些只是C中定义主函数的两种有效方式” - > _almost_。这些(和等价物)只有两个_defined_有效的方法。其他可能存在的每个实施可能会失去可移植性, – chux
因为每个发布的答案都包含事实错误,所以会投票删除投票。所以这篇文章造成的伤害比好的更多。如果没有错误地引用这些标准,请参阅标准实际所说的内容[阅读本文](https://stackoverflow.com/a/31263079/584518)。 – Lundin