C程序需要一个main()吗?

时间:2020-12-30 09:35:05

Well the title says it all. Is a main() function absolutely essential for a C program?

题目说明了一切。main()函数对于C程序来说是绝对必需的吗?

I am asking this because I was looking at the Linux kernel code, and I didn't see a main() function.

我之所以问这个问题,是因为我在查看Linux内核代码,而我没有看到main()函数。

7 个解决方案

#1


62  

No, the ISO C standard states that a main function is only required for a hosted environment (such as one with an underlying OS).

不,ISO C标准规定,主函数只需要托管环境(比如具有底层OS的环境)。

For a freestanding environment like an embedded system (or an operating system itself), it's implementation defined. From C99 5.1.2:

对于独立的环境,如嵌入式系统(或操作系统本身),它的实现是定义的。C99 5.1.2:

Two execution environments are defined: freestanding and hosted. In both cases, program startup occurs when a designated C function is called by the execution environment.

定义了两个执行环境:独立的和托管的。在这两种情况下,当执行环境调用指定的C函数时,都会发生程序启动。

In a freestanding environment (in which C program execution may take place without any benefit of an operating system), the name and type of the function called at program startup are implementation-defined.

在一个独立的环境中(在没有操作系统的任何好处的情况下,C程序执行可能发生),在程序启动时调用的函数的名称和类型是实现定义的。

As to how Linux itself starts, the start point for the Linux kernel is start_kernel though, for a more complete picture of the entire boot process, you should start here.

至于Linux本身是如何启动的,Linux内核的起点是start_kernel,对于整个引导过程的更完整的描述,您应该从这里开始。

#2


9  

The main() function is called by an object file included with the libc. Since the kernel doesn't link against the libc it has its own entry point, written in assembler.

main()函数由包含在libc中的对象文件调用。由于内核没有链接到libc,所以它有自己的入口点,用汇编程序编写。

#3


6  

Well, no, but ...

C99 specifies that main() is called in the hosted environment "at program startup", however, you don't have to use the C runtime support. Your operating system executes image files and starts a program at an address provided by the linker.

C99指定在托管环境中“在程序启动时”调用main(),但是,您不必使用C运行时支持。您的操作系统执行映像文件并以链接器提供的地址启动程序。

If you are willing to write your program to conform to the operating system's requirements rather than C99's, you can do it without main(). The more modern (and complex) the system, though, the more trouble you will have with the C library making assumptions that the standard runtime startup is used.

如果您愿意编写符合操作系统需求的程序,而不是C99的程序,那么您可以不使用main()编写程序。但是,系统越现代(也越复杂),C库假设使用标准运行时启动就越麻烦。

Here is an example for Linux...

这里有一个Linux的例子……

$ cat > nomain.S
.text
_start:
    call    iamnotmain
    movl    $0xfc, %eax
    xorl    %ebx, %ebx
    int     $0x80
.globl _start
$ cat > demo.c

void iamnotmain(void) {
    static char s[] = "hello, world\n";
    write(1, s, sizeof s);
}
$ as -o nomain.o nomain.S
$ cc -c demo.c
$ ld -static nomain.o demo.o -lc
$ ./a.out
hello, world

It's arguably not "a C99 program" now, though, just a "Linux program" with a object module written in C.

它现在可以说不是一个“C99程序”,而是一个用C编写对象模块的“Linux程序”。

#4


2  

Paxdiablo's answer covers two of the cases where you won't encounter a main. Let me add a couple of more:

Paxdiablo给出的答案涵盖了两种你不会遇到主角的情况。我再补充几个:

  • Many plug-in systems for other programs (like, say, browsers or text editors or the like) have no main().
  • 许多用于其他程序的插件系统(如浏览器或文本编辑器等)没有main()。
  • Windows programs written in C have no main(). (They have a WinMain() instead.)
  • 用C编写的Windows程序没有main()。(取而代之的是WinMain())。)

#5


0  

The operating systems loader has to call a single entry point; in the GNU compiler, the entry point is defined in the crt0.o linked object file, the source for this is the assembler file crt0.s - that invokes main() after performing various run-time start-up tasks (such as establishing a stack, static initialisation). So when building an executable that links the default crt0.o, you must have a main(), otherwise you will get a linker error since in crt0.o main() is an unresolved symbol.

操作系统加载程序必须调用单个入口点;在GNU编译器中,入口点在crt0中定义。o链接对象文件,它的源文件是汇编程序文件crt0。在执行各种运行时启动任务(例如建立堆栈、静态初始化)之后,调用main()。因此,当构建一个链接到默认crt0的可执行文件时。o,您必须有一个main(),否则您将得到一个链接器错误,因为在crt0中。o main()是一个未解析的符号。

It would be possible (if somewhat perverse and unnecessary) to modify crt0.s to call a different entry point. Just make sure that you make such an object file specific to your project rather than modifying the default version, or you will break every build on that machine.

修改crt0是可能的(如果有些反常和不必要的话)。s来调用另一个入口点。只需确保您将这样的对象文件指定为您的项目,而不是修改默认的版本,否则您将破坏该机器上的每个构建。

The OS itself has its own C runtime start-up (which will be called from the bootloader) so can call any entry point it wishes. I have not looked at the Linux source, but imagine that it has its own crt0.s that will call whatever the C code entry point is.

操作系统本身有自己的C运行时启动(将从引导加载程序调用),因此可以调用它希望的任何入口点。我没有查看Linux源代码,但是可以想象它有自己的crt0。无论C代码入口点是什么。

#6


0  

main is called by glibc,that is a part of application(ring 3), not the kernel(ring 0).
the driver has another entry point,for example windows driver base on WDM is start from DRIVERENTRY

main由glibc调用,这是应用程序的一部分(ring 3),而不是内核(ring 0)

#7


-2  

In machine language things get executed sequentially, what comes first is executed first. So, the default is for the compiler place a call to you main method to fit the C standard.

在机器语言中,事物按顺序执行,先出现的先执行。默认情况下,编译器会调用主方法来满足C标准。

Your program works like a library, which is a collection of compiled functions. The main difference between a library and a standard executable is that for the second one the compiler generates assembly code which calls one of the functions in your program.

您的程序工作起来就像一个库,它是编译函数的集合。库和标准可执行文件的主要区别在于,对于第二个可执行文件,编译器生成调用程序中的函数的汇编代码。

But you could write assembly code which calls your an arbitrary C program function (the same way calls to library functions work, actually) and this would work the same way other executables do. But the thing is you cannot do it in plain standard C, you have to resort to assembly or even some other compiler specific tricks.

但是,您可以编写汇编代码来调用任意的C程序函数(实际上,调用库函数的方式是一样的),这与其他可执行程序的方式是一样的。但问题是你不能用普通的C语言来做,你必须使用汇编语言或者其他编译器的特殊技巧。

This was intended as a general and superficial explanation, there are some technical differences I avoided on purpose as they don't seem relevant.

这是一种笼统而肤浅的解释,我故意避免了一些技术上的差异,因为它们似乎不相关。

#1


62  

No, the ISO C standard states that a main function is only required for a hosted environment (such as one with an underlying OS).

不,ISO C标准规定,主函数只需要托管环境(比如具有底层OS的环境)。

For a freestanding environment like an embedded system (or an operating system itself), it's implementation defined. From C99 5.1.2:

对于独立的环境,如嵌入式系统(或操作系统本身),它的实现是定义的。C99 5.1.2:

Two execution environments are defined: freestanding and hosted. In both cases, program startup occurs when a designated C function is called by the execution environment.

定义了两个执行环境:独立的和托管的。在这两种情况下,当执行环境调用指定的C函数时,都会发生程序启动。

In a freestanding environment (in which C program execution may take place without any benefit of an operating system), the name and type of the function called at program startup are implementation-defined.

在一个独立的环境中(在没有操作系统的任何好处的情况下,C程序执行可能发生),在程序启动时调用的函数的名称和类型是实现定义的。

As to how Linux itself starts, the start point for the Linux kernel is start_kernel though, for a more complete picture of the entire boot process, you should start here.

至于Linux本身是如何启动的,Linux内核的起点是start_kernel,对于整个引导过程的更完整的描述,您应该从这里开始。

#2


9  

The main() function is called by an object file included with the libc. Since the kernel doesn't link against the libc it has its own entry point, written in assembler.

main()函数由包含在libc中的对象文件调用。由于内核没有链接到libc,所以它有自己的入口点,用汇编程序编写。

#3


6  

Well, no, but ...

C99 specifies that main() is called in the hosted environment "at program startup", however, you don't have to use the C runtime support. Your operating system executes image files and starts a program at an address provided by the linker.

C99指定在托管环境中“在程序启动时”调用main(),但是,您不必使用C运行时支持。您的操作系统执行映像文件并以链接器提供的地址启动程序。

If you are willing to write your program to conform to the operating system's requirements rather than C99's, you can do it without main(). The more modern (and complex) the system, though, the more trouble you will have with the C library making assumptions that the standard runtime startup is used.

如果您愿意编写符合操作系统需求的程序,而不是C99的程序,那么您可以不使用main()编写程序。但是,系统越现代(也越复杂),C库假设使用标准运行时启动就越麻烦。

Here is an example for Linux...

这里有一个Linux的例子……

$ cat > nomain.S
.text
_start:
    call    iamnotmain
    movl    $0xfc, %eax
    xorl    %ebx, %ebx
    int     $0x80
.globl _start
$ cat > demo.c

void iamnotmain(void) {
    static char s[] = "hello, world\n";
    write(1, s, sizeof s);
}
$ as -o nomain.o nomain.S
$ cc -c demo.c
$ ld -static nomain.o demo.o -lc
$ ./a.out
hello, world

It's arguably not "a C99 program" now, though, just a "Linux program" with a object module written in C.

它现在可以说不是一个“C99程序”,而是一个用C编写对象模块的“Linux程序”。

#4


2  

Paxdiablo's answer covers two of the cases where you won't encounter a main. Let me add a couple of more:

Paxdiablo给出的答案涵盖了两种你不会遇到主角的情况。我再补充几个:

  • Many plug-in systems for other programs (like, say, browsers or text editors or the like) have no main().
  • 许多用于其他程序的插件系统(如浏览器或文本编辑器等)没有main()。
  • Windows programs written in C have no main(). (They have a WinMain() instead.)
  • 用C编写的Windows程序没有main()。(取而代之的是WinMain())。)

#5


0  

The operating systems loader has to call a single entry point; in the GNU compiler, the entry point is defined in the crt0.o linked object file, the source for this is the assembler file crt0.s - that invokes main() after performing various run-time start-up tasks (such as establishing a stack, static initialisation). So when building an executable that links the default crt0.o, you must have a main(), otherwise you will get a linker error since in crt0.o main() is an unresolved symbol.

操作系统加载程序必须调用单个入口点;在GNU编译器中,入口点在crt0中定义。o链接对象文件,它的源文件是汇编程序文件crt0。在执行各种运行时启动任务(例如建立堆栈、静态初始化)之后,调用main()。因此,当构建一个链接到默认crt0的可执行文件时。o,您必须有一个main(),否则您将得到一个链接器错误,因为在crt0中。o main()是一个未解析的符号。

It would be possible (if somewhat perverse and unnecessary) to modify crt0.s to call a different entry point. Just make sure that you make such an object file specific to your project rather than modifying the default version, or you will break every build on that machine.

修改crt0是可能的(如果有些反常和不必要的话)。s来调用另一个入口点。只需确保您将这样的对象文件指定为您的项目,而不是修改默认的版本,否则您将破坏该机器上的每个构建。

The OS itself has its own C runtime start-up (which will be called from the bootloader) so can call any entry point it wishes. I have not looked at the Linux source, but imagine that it has its own crt0.s that will call whatever the C code entry point is.

操作系统本身有自己的C运行时启动(将从引导加载程序调用),因此可以调用它希望的任何入口点。我没有查看Linux源代码,但是可以想象它有自己的crt0。无论C代码入口点是什么。

#6


0  

main is called by glibc,that is a part of application(ring 3), not the kernel(ring 0).
the driver has another entry point,for example windows driver base on WDM is start from DRIVERENTRY

main由glibc调用,这是应用程序的一部分(ring 3),而不是内核(ring 0)

#7


-2  

In machine language things get executed sequentially, what comes first is executed first. So, the default is for the compiler place a call to you main method to fit the C standard.

在机器语言中,事物按顺序执行,先出现的先执行。默认情况下,编译器会调用主方法来满足C标准。

Your program works like a library, which is a collection of compiled functions. The main difference between a library and a standard executable is that for the second one the compiler generates assembly code which calls one of the functions in your program.

您的程序工作起来就像一个库,它是编译函数的集合。库和标准可执行文件的主要区别在于,对于第二个可执行文件,编译器生成调用程序中的函数的汇编代码。

But you could write assembly code which calls your an arbitrary C program function (the same way calls to library functions work, actually) and this would work the same way other executables do. But the thing is you cannot do it in plain standard C, you have to resort to assembly or even some other compiler specific tricks.

但是,您可以编写汇编代码来调用任意的C程序函数(实际上,调用库函数的方式是一样的),这与其他可执行程序的方式是一样的。但问题是你不能用普通的C语言来做,你必须使用汇编语言或者其他编译器的特殊技巧。

This was intended as a general and superficial explanation, there are some technical differences I avoided on purpose as they don't seem relevant.

这是一种笼统而肤浅的解释,我故意避免了一些技术上的差异,因为它们似乎不相关。