我从同事那里了解到,无需编写main()
函数即可编写和执行C程序。可以这样做:
my_main.c
/* Compile this with gcc -nostartfiles */
#include <stdlib.h>
void _start() {
int ret = my_main();
exit(ret);
}
int my_main() {
puts("This is a program without a main() function!");
return 0;
}
使用此命令编译:
gcc -o my_main my_main.c –nostartfiles
使用以下命令运行它:
./my_main
何时需要做这种事情?有没有真实世界的场景,这会有用吗?
答案 0 :(得分:91)
符号_start
是程序的入口点。也就是说,该符号的地址是程序启动时跳转到的地址。通常,名为_start
的函数由名为crt0.o
的文件提供,该文件包含C运行时环境的启动代码。它设置一些东西,填充参数数组argv
,计算有多少参数,然后调用main
。 main
返回后,系统会调用exit
。
如果程序不想使用C运行时环境,则需要为_start
提供自己的代码。例如,Go编程语言的参考实现是这样做的,因为它们需要一个非标准的线程模型,这需要一些神奇的堆栈。当您想编写非常小的程序或执行非常规事物的程序时,提供自己的_start
也很有用。
答案 1 :(得分:43)
虽然从程序员的角度来看main
是程序的入口点,_start
是OS操作系统的常用入口点(从OS启动程序后执行的第一条指令) )
在典型的C,尤其是C ++程序中,在执行main之前已经完成了很多工作。 特别是像全局变量初始化这样的东西。 Here你可以找到_start()
和main()
之间发生的所有事情的良好解释,以及在main再次退出之后(见下面的评论)
它的必要代码通常由编译器编写者在启动文件中提供,但是使用标志–nostartfiles
,你基本上告诉编译器:“不要打扰给我标准的启动文件,让我完全控制什么从一开始就发生了“。
这有时是必要的,并且经常在嵌入式系统上使用。例如。如果您没有操作系统,则必须在初始化全局对象之前手动启用内存系统的某些部分(例如缓存)。
答案 2 :(得分:1)
什么时候需要做这种事情?
当您想要自己的程序启动代码时。
main
不是C程序的第一项,_start
是幕后的第一项。
Linux中的示例:
_start: # _start is the entry point known to the linker
xor %ebp, %ebp # effectively RBP := 0, mark the end of stack frames
mov (%rsp), %edi # get argc from the stack (implicitly zero-extended to 64-bit)
lea 8(%rsp), %rsi # take the address of argv from the stack
lea 16(%rsp,%rdi,8), %rdx # take the address of envp from the stack
xor %eax, %eax # per ABI and compatibility with icc
call main # %edi, %rsi, %rdx are the three args (of which first two are C standard) to main
mov %eax, %edi # transfer the return of main to the first argument of _exit
xor %eax, %eax # per ABI and compatibility with icc
call _exit # terminate the program
在现实世界中,这是否有用?
如果您是故意的,请实施我们自己的_start
:
是的,在我使用过的大多数商业嵌入式软件中,我们都需要针对自己的特定内存和性能要求实施自己的_start
。
如果您的意思是,请放下main
函数并将其更改为其他内容:
不,我认为这样做没有任何好处。
答案 3 :(得分:-1)
_start()
是由特殊标头指向的函数,仅供计算机理解。但是这很重要,因为我们应该了解计算机背面的情况。