C 中的 main() 方法是如何工作的?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19419569/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How does the main() method work in C?
提问by Ritesh
I know there are two different signatures to write the main method -
我知道有两种不同的签名来编写主要方法 -
int main()
{
//Code
}
or for handling command line argument, we write it as-
或者为了处理命令行参数,我们把它写成-
int main(int argc, char * argv[])
{
//code
}
In C++
I know we can overload a method, but in C
how does the compiler handle these two different signatures of main
function?
在C++
我知道我们可以重载的方法,但是在C
编译器如何处理这两个不同的签名main
功能?
回答by Kaz
Some of the features of the C language started out as hacks which just happened to work.
C 语言的一些特性最初只是碰巧起作用的 hack。
Multiple signatures for main, as well as variable-length argument lists, is one of those features.
main 的多个签名以及可变长度的参数列表是这些功能之一。
Programmers noticed that they can pass extra arguments to a function, and nothing bad happens with their given compiler.
程序员注意到他们可以将额外的参数传递给函数,并且他们给定的编译器不会发生任何不好的事情。
This is the case if the calling conventions are such that:
如果调用约定如下:
- The calling function cleans up the arguments.
- The leftmost arguments are closer to the top of the stack, or to the base of the stack frame, so that spurious arguments do not invalidate the addressing.
- 调用函数清除参数。
- 最左边的参数更靠近堆栈顶部,或堆栈帧的底部,因此虚假参数不会使寻址无效。
One set of calling conventions which obeys these rules is stack-based parameter passing whereby the caller pops the arguments, and they are pushed right to left:
一组遵守这些规则的调用约定是基于堆栈的参数传递,调用者由此弹出参数,并将它们从右向左推送:
;; pseudo-assembly-language
;; main(argc, argv, envp); call
push envp ;; rightmost argument
push argv ;;
push argc ;; leftmost argument ends up on top of stack
call main
pop ;; caller cleans up
pop
pop
In compilers where this type of calling convention is the case, nothing special need to be done to support the two kinds of main
, or even additional kinds. main
can be a function of no arguments, in which case it is oblivious to the items that were pushed onto the stack. If it's a function of two arguments, then it finds argc
and argv
as the two topmost stack items. If it's a platform-specific three-argument variant with an environment pointer (a common extension), that will work too: it will find that third argument as the third element from the top of the stack.
在这种类型的调用约定是这种情况的编译器中,不需要做任何特殊的事情来支持这两种main
甚至额外的类型。main
可以是一个没有参数的函数,在这种情况下,它不会注意到被压入堆栈的项目。如果它是一个有两个参数的函数,那么它会找到argc
和argv
作为两个最顶层的堆栈项。如果它是具有环境指针(通用扩展)的特定于平台的三参数变体,那也将起作用:它将找到第三个参数作为堆栈顶部的第三个元素。
And so a fixed call works for all cases, allowing a single, fixed start-up module to be linked to the program. That module could be written in C, as a function resembling this:
因此,固定调用适用于所有情况,允许将单个固定启动模块链接到程序。该模块可以用 C 编写,作为一个类似于以下的函数:
/* I'm adding envp to show that even a popular platform-specific variant
can be handled. */
extern int main(int argc, char **argv, char **envp);
void __start(void)
{
/* This is the real startup function for the executable.
It performs a bunch of library initialization. */
/* ... */
/* And then: */
exit(main(argc_from_somewhere, argv_from_somewhere, envp_from_somewhere));
}
In other words, this start module just calls a three-argument main, always. If main takes no arguments, or only int, char **
, it happens to work fine, as well as if it takes no arguments, due to the calling conventions.
换句话说,这个 start 模块总是调用一个三参数的 main。如果 main 没有参数,或者只有int, char **
,它碰巧工作正常,如果它没有参数,由于调用约定。
If you were to do this kind of thing in your program, it would be nonportable and considered undefined behavior by ISO C: declaring and calling a function in one manner, and defining it in another. But a compiler's startup trick does not have to be portable; it is not guided by the rules for portable programs.
如果您要在您的程序中执行此类操作,它将是不可移植的,并被 ISO C 视为未定义行为:以一种方式声明和调用函数,并以另一种方式定义它。但是编译器的启动技巧不一定是可移植的;它不受可移植程序规则的指导。
But suppose that the calling conventions are such that it cannot work this way. In that case, the compiler has to treat main
specially. When it notices that it's compiling the main
function, it can generate code which is compatible with, say, a three argument call.
但是假设调用约定不能以这种方式工作。在这种情况下,编译器必须main
特殊对待。当它注意到它正在编译main
函数时,它可以生成与三参数调用兼容的代码。
That is to say, you write this:
也就是说,你这样写:
int main(void)
{
/* ... */
}
But when the compiler sees it, it essentially performs a code transformation so that the function which it compiles looks more like this:
但是当编译器看到它时,它本质上会执行代码转换,以便它编译的函数看起来更像这样:
int main(int __argc_ignore, char **__argv_ignore, char **__envp_ignore)
{
/* ... */
}
except that the names __argc_ignore
don't literally exist. No such names are introduced into your scope, and there won't be any warning about unused arguments.
The code transformation causes the compiler to emit code with the correct linkage which knows that it has to clean up three arguments.
除了这些名字__argc_ignore
实际上并不存在。不会在您的作用域中引入此类名称,并且不会对未使用的参数发出任何警告。代码转换使编译器发出具有正确链接的代码,该链接知道它必须清除三个参数。
Another implementation strategy is for the compiler or perhaps linker to custom-generate the __start
function (or whatever it is called), or at least select one from several pre-compiled alternatives. Information could be stored in the object file about which of the supported forms of main
is being used. The linker can look at this info, and select the correct version of the start-up module which contains a call to main
which is compatible with the program's definition. C implementations usually have only a small number of supported forms of main
so this approach is feasible.
另一种实现策略是编译器或链接器自定义生成__start
函数(或任何调用的函数),或者至少从几个预编译的替代方案中选择一个。可以在目标文件中存储关于main
正在使用哪种支持形式的信息。链接器可以查看此信息,并选择包含main
与程序定义兼容的调用的启动模块的正确版本。C 实现通常只有少量支持的形式,main
因此这种方法是可行的。
Compilers for the C99 language always have to treat main
specially, to some extent, to support the hack that if the function terminates without a return
statement, the behavior is as if return 0
were executed. This, again, can be treated by a code transformation. The compiler notices that a function called main
is being compiled. Then it checks whether the end of the body is potentially reachable. If so, it inserts a return 0;
C99 语言的编译器在main
某种程度上总是要特殊对待,以支持如果函数在没有return
语句的情况下终止,则行为就像return 0
被执行一样。这同样可以通过代码转换来处理。编译器注意到main
正在编译一个被调用的函数。然后它检查主体的末端是否可能可达。如果是这样,它会插入一个return 0;
回答by Sadique
There is NO overloading of main
even in C++. Main function is the entry point for a program and only a single definition should exist.
main
即使在 C++ 中也没有重载。Main 函数是程序的入口点,应该只存在一个定义。
For Standard C
对于标准 C
For a hosted environment (that's the normal one), the C99 standard says:
5.1.2.2.1 Program startup
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 ofint
and with no parameters:int main(void) { /* ... */ }
or with two parameters (referred to here as
argc
andargv
, though any names may be used, as they are local to the function in which they are declared):int main(int argc, char *argv[]) { /* ... */ }
or equivalent;9)or in some other implementation-defined manner.
9)Thus,
int
can be replaced by a typedef name defined asint
, or the type ofargv
can be written aschar **argv
, and so on.
对于托管环境(这是正常环境),C99 标准说:
5.1.2.2.1 程序启动
程序启动时调用的函数名为
main
. 实现声明没有此函数的原型。它应定义为返回类型int
并且不带参数:int main(void) { /* ... */ }
或带有两个参数(这里称为
argc
andargv
,尽管可以使用任何名称,因为它们对于声明它们的函数是本地的):int main(int argc, char *argv[]) { /* ... */ }
或同等学历; 9)或以其他一些实现定义的方式。
9)因此,
int
可以用定义为 的 typedef 名称代替int
,或者argv
可以将的类型写为char **argv
,依此类推。
For standard C++:
对于标准 C++:
3.6.1 Main function [basic.start.main]
1 A program shall contain a global function called main, which is the designated start of the program. [...]
2 An implementation shall notpredefine the main function. This function shall not be overloaded. It shall have a return type of type int, but otherwise its type is implementation defined. All implementations shall allow both of the following definitions of main:
int main() { /* ... */ }
and
int main(int argc, char* argv[]) { /* ... */ }
3.6.1 主函数【basic.start.main】
1 程序应包含一个名为 main 的全局函数,它是程序的指定开始。[...]
2 实现不应预定义主函数。该函数不得重载。它应该有一个 int 类型的返回类型,否则它的类型是实现定义的。所有实现都应允许以下两个 main 定义:
int main() { /* ... */ }
和
int main(int argc, char* argv[]) { /* ... */ }
The C++ standard explicitly says "It [the main function] shall have a return type of type int, but otherwise its type is implementation defined", and requires the same two signatures as the C standard.
C++ 标准明确规定“它 [主函数] 应具有 int 类型的返回类型,否则其类型是实现定义的”,并且需要与 C 标准相同的两个签名。
In a hosted environment(A C environment which also supports the C libraries) - the Operating System calls main
.
在托管环境(也支持 C 库的 AC 环境)中 - 操作系统调用main
.
In a non-hosted environment(One intended for embedded applications) you can always change the entry point (or exit) of your program using the pre-processor directives like
在非托管环境(用于嵌入式应用程序的环境)中,您始终可以使用预处理器指令更改程序的入口点(或出口),例如
#pragma startup [priority]
#pragma exit [priority]
Where priority is an optional integral number.
其中优先级是一个可选的整数。
Pragma startup executes the function before the main (priority-wise) and pragma exit executes the function after the main function. If there is more than one startup directive then priority decides which will execute first.
Pragma startup 在 main (优先级)之前执行函数, pragma exit 在 main 函数之后执行函数。如果有多个启动指令,则优先级决定哪个将首先执行。
回答by user694733
There is no need for overloading. Yes, there are 2 versions, but only one can be used at the time.
没有必要超载。是的,有2个版本,但当时只能使用一个。
回答by 6502
This is one of the strange asymmetries and special rules of the C and C++ language.
这是 C 和 C++ 语言奇怪的不对称和特殊规则之一。
In my opinion it exists only for historical reasons and there's no real serious logic behind it. Note that main
is special also for other reasons (for example main
in C++ cannot be recursive and you cannot take its address and in C99/C++ you are allowed to omit a final return
statement).
在我看来,它的存在只是出于历史原因,背后没有真正严肃的逻辑。请注意,main
由于其他原因,这也是特殊的(例如,main
在 C++ 中不能递归并且您不能获取其地址,并且在 C99/C++ 中您可以省略最终return
语句)。
Note also that even in C++ it's not an overload... either a program has the first form or it has the second form; it cannot have both.
另请注意,即使在 C++ 中,它也不是重载……程序要么具有第一种形式,要么具有第二种形式;它不能两者兼而有之。
回答by Keith Thompson
What's unusual about main
isn't that it can be defined in more than one way, it's that it can onlybe defined in one of two different ways.
不寻常main
的不是它可以以多种方式定义,而是它只能以两种不同方式之一进行定义。
main
is a user-defined function; the implementation doesn't declare a prototype for it.
main
是用户定义的函数;实现没有为其声明原型。
The same thing is true for foo
or bar
, but you can define functions with those names any way you like.
同样的事情也适用于foo
or bar
,但您可以以任何您喜欢的方式定义具有这些名称的函数。
The difference is that main
is invoked by the implementation (the runtime environment), not just by your own code. The implementation isn't limited to ordinary C function call semantics, so it can (and must) deal with a few variations -- but it's not required to handle infinitely many possibilities. The int main(int argc, char *argv[])
form allows for command-line arguments, and int main(void)
in C or int main()
in C++ is just a convenience for simple programs that don't need to process command-line arguments.
不同之处在于它main
是由实现(运行时环境)调用的,而不仅仅是由您自己的代码调用。该实现不限于普通的 C 函数调用语义,因此它可以(并且必须)处理一些变体——但不需要处理无限多种可能性。该int main(int argc, char *argv[])
形式允许命令行参数,int main(void)
在 C 或int main()
C++ 中只是为不需要处理命令行参数的简单程序提供便利。
As for how the compiler handles this, it depends on the implementation. Most systems probably have calling conventions that make the two forms effectively compatible, and any arguments passed to a main
defined with no parameters are quietly ignored. If not, it wouldn't be difficult for a compiler or linker to treat main
specially. If you're curious how it works on your system, you might look at some assembly listings.
至于编译器如何处理,这取决于实现。大多数系统可能具有使两种形式有效兼容的调用约定,并且传递给main
没有参数的定义的任何参数都被悄悄地忽略。如果没有,编译器或链接器进行main
特殊处理并不困难。如果您对它如何在您的系统上工作感到好奇,您可以查看一些程序集列表。
And like many things in C and C++, the details are largely a result of history and arbitrary decisions made by the designers of the languages and their predecessors.
就像 C 和 C++ 中的许多事情一样,细节在很大程度上是语言设计者及其前辈做出的历史和任意决定的结果。
Note that both C and C++ both permit other implementation-defined definitions for main
-- but there's rarely any good reason to use them. And for freestanding implementations(such as embedded systems with no OS), the program entry point is implementation-defined, and isn't necessarily even called main
.
请注意,C 和 C++ 都允许其他实现定义的定义main
——但很少有充分的理由使用它们。对于独立的实现(例如没有操作系统的嵌入式系统),程序入口点是实现定义的,甚至不一定调用main
.
回答by AndersK
The main
is just a name for a starting address decided by the linker where main
is the default name. All function names in a program are starting addresses where the function starts.
这main
只是由链接器决定的起始地址的名称,其中main
是默认名称。程序中的所有函数名都是函数开始的起始地址。
The function arguments are pushed/popped on/from the stack so if there are no arguments specified for the function there are no arguments pushed/popped on/off the stack. That is how main can work both with or without arguments.
函数参数被推入/弹出堆栈,因此如果没有为函数指定参数,则不会从堆栈中推入/弹出参数。这就是 main 可以在有参数或没有参数的情况下工作的方式。
回答by varepsilon
A similar question was asked before: Why does a function with no parameters (compared to the actual function definition) compile?
之前问过一个类似的问题:为什么没有参数的函数(与实际函数定义相比)编译?
One of the top-ranked answers was:
排名最高的答案之一是:
In C
func()
means that you can pass anynumber of arguments. If you want no arguments then you have to declare asfunc(void)
在 C 中
func()
意味着您可以传递任意数量的参数。如果你不想要参数,那么你必须声明为func(void)
So, I guess it's how main
is declared (if you can apply the term "declared" to main
). In fact you can write something like this:
所以,我想这main
就是声明的方式(如果您可以将“声明”一词应用于main
)。事实上,你可以这样写:
int main(int only_one_argument) {
// code
}
and it will still compile and run.
它仍然会编译和运行。
回答by manish
Well, the two different signatures of the same function main() comes in picture only when you want them so, I mean if your programm needs data before any actual processing of your code you may pass them via use of -
好吧,同一函数 main() 的两个不同签名只有在您需要它们时才会出现,我的意思是,如果您的程序在对代码进行任何实际处理之前需要数据,您可以通过使用 -
int main(int argc, char * argv[])
{
//code
}
where the variable argc stores the count of data that is passed and argv is an array of pointers to char which points to the passed values from console. Otherwise it's always good to go with
其中变量 argc 存储传递的数据计数,argv 是指向 char 的指针数组,指向从控制台传递的值。否则一起去总是好的
int main()
{
//Code
}
However in any case there can be one and only one main() in a programm, as because that's the only point where from a program starts its execution and hence it can not be more than one. (hope its worthy)
然而,在任何情况下,程序中都可以只有一个 main(),因为这是程序开始执行的唯一点,因此它不能超过一个。(希望它值得)
回答by gautam
You do not need to override this.because only one will used at a time.yes there are 2 different version of main function
你不需要覆盖这个。因为一次只会使用一个。是的,有 2 个不同版本的 main 函数