main()函数在C或C ++中的原型? [英] Prototype of main() function in c or c++?

查看:86
本文介绍了main()函数在C或C ++中的原型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在阅读一个教程,并且在那儿读过,我们不能定义void main(),因为原型已经定义为int main() or int main(int argc , char *argv),这只是在C中定义主函数的两种有效方法. 因此这些原型是在哪个头文件或库文件中设置的 在float main()的情况下,编译器如何给我带来错误?背后的机制是什么,我的意思是这是语法错误还是为main()定义了一些原型?

I was going through a tutorial and i have read there, that we can not define void main() as the prototype is already defined as int main() or int main(int argc , char *argv) these are only two valid way to define main function in C. so in which header file or library files these prototype are set I don't how compiler giving me error in case of float main() what is the mechanism behind, I mean is it a syntax error or some prototype defined for main()?

请用简单的语言提出答案.

please suggest an answer in easy language.

推荐答案

您的教程并非完全错误,但是它描述了非常严格的实现的行为,比C标准更严格地要求实现会.

Your tutorial is not exactly wrong, but it describes the behavior of a very strict implementation, stricter than the C standard requires implementations to be.

C标准对程序启动时调用的函数的说明只是要求某些可能性才能起作用.并不是说 not 不需要其他功能.同样,C标准很少说明编译器错误.现代C实现通常远远超出诊断标准的要求;对于他们来说,支持他们将接受的程序集的许多扩展也是很常见的.

What the C standard says about the function that is called on program start is just that certain possibilities are required to work. It doesn't say that other things are required not to work. Also, the C standard says very little about compiler errors. It is common for modern C implementations to go well beyond the standard's requirements for diagnostics; it is also common for them to support many extensions to the set of programs they will accept.

在托管"环境中,该环境提供了标准C库的所有功能,赋予在程序开头调用的功能的程序的名称和签名int main(void)int main(int argc, char **argv)符合 em>.他们必须工作.但是该标准还允许将该函数声明为以其他一些实现定义的方式",并且存在许多替代名称和签名:我仅列出一些最常见的名称和签名.

In a "hosted" environment, one that provides all of the facilities of the standard C library, programs that give the function called on program start the name and signature int main(void) or int main(int argc, char **argv) are conforming. They are required to work. But the standard permits this function to be declared "in some other implementation-defined manner" as well, and many alternative names and signatures exist: I'm just going to list a few of the most common ones.

  • int main(int argc, char **argv, char **envp)
  • void main(void)
  • int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
  • int main(int argc, char **argv, char **envp)
  • void main(void)
  • int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)

如果您的实现文档证明它支持这些替代入口点名称或签名之一,则完全可以使用它. (您的程序不会严格符合",但是几乎没有真正的程序是严格符合"的,所以不必担心.)

If your implementation documents that it supports one of these alternative entry point names or signatures, it's perfectly OK to use it. (Your program will not be "strictly conforming", but almost no real programs are "strictly conforming", so don't worry about it.)

在独立"环境中,不提供所有标准C库,在程序启动时调用的函数的名称和签名取决于实现—您可能拥有使用古怪的东西,例如EFI_STATUS efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable). 但是,int main(void)正常工作,而int main(int argc, char **argv)正常工作(在运行时收到的参数可能是垃圾).

In a "freestanding" environment, which doesn't provide all of the standard C library, the name and signature of the function called on program start are left up to the implementation—you might have to use something wacky like EFI_STATUS efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable). However, it is common for int main(void) to work, and somewhat less common for int main(int argc, char **argv) to work (the arguments received at runtime may be garbage).

现在,如果您拥有托管环境,并且使用了未证明有效的入口点函数名称和/或签名,该怎么办? C标准指出,在这种情况下,您的程序具有未定义的行为-允许发生任何任何事情.实际发生的一些常见事情是:

Now, what happens if you have a hosted environment and you use an entry-point function name and/or signature that isn't documented to work? The C standard says that your program has undefined behavior in this case—anything at all is allowed to happen. Some common things that will actually happen are:

  • 编译器会发出错误,或至少发出警告.执行此操作时,无法从任何头文件中获取正确的原型;相反,据说正确的原型是在编译器的内置中构建的,在编译器自己的源代码中定义.如今,许多C库函数以及main都是这种情况.演示:

  • The compiler does issue an error, or at least a warning. It's not getting the correct prototype from any header file when it does this; rather, the correct prototype is said to be built in to the compiler, defined in the compiler's own source code. Nowadays this is the case for many C library functions as well as main. A demonstration:

$ 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,因为这会关闭 off 扩展名可能还需要,并且还可以在系统标题中显示错误.)

(For historical reasons, GCC is not nearly as picky about people's code as it could be; many of the things that, from a modern perspective, should be errors are merely warnings, and not even warnings that are on by default. If you're writing new code from scratch and you use GCC, I recommend using -std=gnu11 -Wall -Wextra -Wpedantic -Werror options basically always. Not -std=c11, though, because that turns off extensions that you may need, and can also expose bugs in the system headers.)

程序无法链接.例如,这就是发生的情况,如果您尝试组成自己的名字,而不是称它为main:

The program fails to link. This is what happens, for instance, if you try to make up your own name, instead of calling it 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,其最后一行类似于

This is one of the more cryptic errors you can get out of the linker, so I'll unpack it a little. Have you ever wondered how main gets called? It's surprisingly simple: there's a function provided by the C library, conventionally called _start, whose last line is something like

exit(main(argc, argv, environ));

由于历史原因,此功能未与libc.so中的大部分C库捆绑在一起.它位于一个单独的目标文件crt1.o中,当要求其链接程序时,编译器会自动将其插入(就像它自动添加到-lc一样).因此,当您不定义main时,将无法满足从_start引用main的情况,并且链接会失败.

For historical reasons, this function isn't bundled with the bulk of the C library in libc.so. It's in a separate object file, crt1.o, that the compiler automatically pulls in when asked to link a program (just like it automatically tacks on an -lc). Thus, when you don't define main, the reference to main from _start is unsatisfied and the link fails.

(好的,_start是怎么被调用的?那是您进入更深层次的魔术的地方.问另一个问题.)

(OK, how does _start get called? That's where you get into deeper magic. Ask another question.)

最后,该程序可能会编译并正确链接,甚至出现才能正常工作-但看上去更难,您会发现它的行为不正确.如果在Unix系统上使用void main(void),就会发生这种情况. (首先,Windows以外的所有托管环境如今都是Unix系统.)

Finally, the program may compile and link fine and even appear to work correctly — but look harder and you discover that it is misbehaving. This is what happens if you use void main(void) on a Unix system. (To first order, all hosted environments other than Windows are Unix systems nowadays.)

$ cat > test.c <<\!
extern int puts(const char *);
void main(void) { puts("hello world"); }
!
$ gcc -std=gnu11 test.c
$ ./a.out
hello world

没有-Wall,不是编译器的窥视,程序运行正常……还是这样做?

Without -Wall, not a peep from the compiler, and the program ran fine...or did it?

$ ./a.out ; echo $?
hello world
12

应该从main返回的值成为程序的 退出状态 ,它出现在外壳变量$?中.如果正确声明了main返回int,并且末尾有一个return 0;,则echo $?会打印0.12是哪里来的?可能是puts的返回值,编译器在从main返回之前没有费心清除返回值寄存器.

The value that's supposed to be returned from main becomes the program's exit status, which appears in the shell variable $?. If main had been properly declared to return int, and there had been a return 0; at the end, the echo $? would have printed 0. Where did 12 come from? Probably it was the return value of puts, which the compiler did not bother clearing out of the return-value register before returning from main.

很容易注意到这个错误,但是一个错误,第一个尝试编写涉及您的程序的Shell脚本的人会被您烦恼.

It's easy to not notice this bug, but it is a bug and the first person who tries to write a shell script that involves your program will be annoyed with you.

一些有关退出状态的脚注,主要是针对学步车的人

Some footnotes about the exit status, mainly for pedants:

  1. 在C ++中以及从1999年标准开始的C中,只要您正确声明,就可以从技术上允许在main的末尾省略任何显式的return 0;,但是我认为应该依靠它风格很差.

  1. In C++, and in C starting with the 1999 standard, you are technically allowed to omit any explicit return 0; at the end of main as long as you declare it correctly, but I think relying on this is poor style.

在Unix的许多实现中,但不是全部,在$?中显示的值只是从main返回的值的低7位或8位.这是用于检索子进程的退出状态的系统调用中的限制, waitpid .

On many but not all implementations of Unix, the value that shows up in $? will be only the low seven or eight bits of the value returned from main. This is a limitation in the system call used to retrieve the exit status of a child process, waitpid.

一个严格符合标准的ISO C程序只能从main返回三个值:0,EXIT_SUCCESSEXIT_FAILURE;后两个常量在stdlib.h中声明.保证从main返回零的效果与返回EXIT_SUCCESS效果相同,但是不相同保证相等.

A strictly conforming ISO C program can only return three values from main: 0, EXIT_SUCCESS, and EXIT_FAILURE; the latter two constants are declared in stdlib.h. The effect of returning zero from main is guaranteed to be the same as the effect of returning EXIT_SUCCESS, but the values are not guaranteed to be equal.

在实践中,返回至少0、1和2是安全的,并且EXIT_SUCCESS != 0和/或EXIT_FAILURE != 1早在空中就已经存在了,所以不要担心它.

In practice, it is safe to return at least 0, 1, and 2, and the implementations where EXIT_SUCCESS != 0 and/or EXIT_FAILURE != 1 have long since gone to the great bit bucket in the sky, so don't worry about it.

这篇关于main()函数在C或C ++中的原型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆