_start()在C中有什么用途?

_start()在C中有什么用途?,c,gcc,startup,C,Gcc,Startup,我从我的同事那里学到了不用编写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!");

我从我的同事那里学到了不用编写
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

什么时候需要做这种事情?在现实世界中,这是否有用?

虽然从程序员的角度来看,
main
是程序的入口点,但从操作系统的角度来看,
\u start
是通常的入口点(从操作系统启动程序后执行的第一条指令)

在典型的C语言中,尤其是C++程序,在执行进入主程序之前已经做了大量的工作。特别是全局变量的初始化。您可以找到一个很好的解释,说明在

\u start()
main()
之间以及在main再次退出之后发生的一切(参见下面的注释)。
这方面的必要代码通常由编译器编写者在启动文件中提供,但使用标志
–nostartfiles
基本上告诉编译器:“不要麻烦给我标准启动文件,让我从一开始就完全控制正在发生的事情”


这有时是必要的,通常用于嵌入式系统。例如,如果您没有操作系统,并且在初始化全局对象之前必须手动启用内存系统的某些部分(例如缓存)。

符号
\u start
是程序的入口点。也就是说,该符号的地址是程序启动时跳转到的地址。通常,名为
\u start
的函数由名为
crt0.o
的文件提供,该文件包含C运行时环境的启动代码。它设置一些东西,填充参数数组
argv
,计算有多少个参数,然后调用
main
。在
main
返回后,调用
exit


如果程序不想使用C运行时环境,它需要为
\u start
提供自己的代码。例如,Go编程语言的参考实现就是这样做的,因为它们需要一个非标准的线程模型,这需要堆栈的一些魔力。当您想要编写非常小的程序或执行非常规操作的程序时,提供自己的
\u start
也很有用

什么时候需要做这种事情

当您想要自己的程序启动代码时

main
不是C程序的第一个条目,
\u 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
在现实世界中,这是否有用

如果您的意思是,实施我们自己的
\u start

是的,在我使用过的大多数商用嵌入式软件中,我们需要根据特定的内存和性能要求实现我们自己的
\u start

如果您的意思是,请删除
main
函数并将其更改为其他内容:

不,我看不出这样做有什么好处

很好地概述了在
main
之前程序启动期间发生的情况。特别是,它表明从操作系统的角度来看,
\u start
是程序的实际入口点

它是程序中开始计数的第一个地址

那里的代码调用一些C运行时库例程只是为了做一些内务处理,然后调用
main
,然后关闭并调用
exit
,返回任何退出代码
main


一幅画胜过千言万语:



附言:这个答案是从SO那里移植的,SO作为这个答案的副本关闭了它。

远程相关:演示程序如何启动的一些内部工作原理的经典文章:。这本书讨论了
\u start()
main()
之外的其他内容的一些细节。C语言本身没有提到
\u start
,也没有提到除
main
之外的任何入口点(入口点的名称是为独立(嵌入式)实现定义的)请注意,此
\u start
不安全,在调用
my_main
时违反了ABI;您告诉编译器这是一个普通函数,但实际上它是在堆栈指针已经对齐的情况下输入的(例如,在x86-64上,RSP%16==0),而不是像在推送8字节返回地址的
调用之后进入普通函数时那样的RSP%16==8。您可以使用
\uu attribute\uuuuuuu((强制对齐参数指针))
\u start
修复此问题,以告诉GCC堆栈指针在进入该“函数”时可能“未对齐”,如现代Linux发行版中所示,如果
my\u main
使用scanf或printf(或任何可变函数),这将导致崩溃使用
floar
double
FP arg。另一个例子是Linux的动态链接器/加载器,它定义了自己的“启动”。@BlueMoon但是
\u start
也来自对象文件
crt0.o
。@ThomasMatthews标准没有指定
\u start
;实际上,它根本没有指定在调用
main
之前会发生什么,它只是指定调用
main
时必须满足哪些条件。更像是一种约定,入口点是
\u start
,可以追溯到过去。“Go编程语言的参考实现之所以这样做,是因为它们需要一个非标准线程模型”crt0.o是特定于C的(crt->C运行时)。没有理由期望它被用于任何其他语言。Go的线程模型是完全标准的compliant@SteveCox许多编程语言都构建在C运行时之上,因为这样更容易实现语言。去是不行的