【问题标题】:Is a main() required for a C program?C 程序是否需要 main()?
【发布时间】:2011-05-06 01:13:56
【问题描述】:

好吧,标题说明了一切。 main() 函数对于 C 程序来说是绝对必要的吗?

我问这个是因为我正在查看 Linux 内核代码,但我没有看到 main() 函数。

【问题讨论】:

  • Jens 称其为复制品,这实在是太过分了。
  • 我自己并不认为这是一个骗局。那是令人讨厌的面试问题之一,这似乎是关于 Linux 如何启动的更严肃的询问。
  • 嗯,我只是在看Linux源代码,问题突然出现了。我在发布之前做了搜索,我没有想到如何避免main()和main的必要性,是一样的问题。诚实的错误。
  • 这根本不是一个错误,@Mad-scientist。 Jens 正在大发雷霆。

标签: c language-lawyer


【解决方案1】:

不,ISO C 标准规定 main 函数仅在托管环境(例如具有底层操作系统的环境)中需要。

对于像嵌入式系统(或操作系统本身)这样的独立环境,它是由实现定义的。来自C995.1.2

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

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

至于 Linux 本身是如何启动的,Linux 内核的起点是start_kernel,不过要更完整地了解整个启动过程,您应该从 here 开始。

【讨论】:

  • 实现者想要的任何地方。
  • @Mad-scientist,请参阅start_kernelinit/main.c 中的更新。
  • 其实再看一遍就明白了谢谢。
  • 即使 C 标准对独立实现只字未提,并坚持每个程序都必须有一个 main,Linux 内核仍然不需要,因为,天哪,它不是一个符合程序。这意味着您无法保证您可以使用 MSVC 在您的 Windows 机器上编译它并让它正确执行——但是您没想到能够做到,是吗?这里的大多数人都不了解标准——如果你做某些事情,它会做出某些保证,但如果你不这样做,它就不能阻止事情的发生。
  • @Mad-scientist 不,你不明白。 main 不是“固定起点”,它是程序中的任意位置,由链接器记录在可执行文件中。但是可以选择其他名称,另外一个是用于 Linux 的。 C 标准在这里是无关紧要的,因为 Linux 内核不是一个符合标准的程序,它不是由符合标准的实现编译的——当然不是用于构建 Linux 内核的标志。一些愚蠢的人说“但它可能会引爆热核炸弹”——他们不明白标准是如何运作的。
【解决方案2】:

嗯,不,但是...

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

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

这是一个 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

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

【讨论】:

  • +1。我仍然认为这是一个 C99 程序,只是一个独立的程序。标准中没有任何内容表明您必须使用操作系统,因为它存在:-)
【解决方案3】:

main() 函数由 libc 中包含的目标文件调用。由于内核不链接到 libc,因此它有自己的入口点,用汇编程序编写。

【讨论】:

    【解决方案4】:

    Paxdiablo 的answer 涵盖了您不会遇到主的两种情况。让我再补充几个:

    • 许多其他程序(例如浏览器或文本编辑器等)的插件系统没有main()
    • 用 C 编写的 Windows 程序没有main()。 (他们有一个WinMain()。)

    【讨论】:

    • Windows 程序有 WinMain,因为 main() 函数会在存根中,它会做一些事情然后调用 WinMain。
    • 就像 GCC 程序,比如说,有一个 main(),因为 _start()(我认为)在 libc 的样板代码中,它做了一些事情,然后调用 main()。我不确定你的意思是什么。还是你以为main()是操作系统直接调用的?
    【解决方案5】:

    操作系统加载程序必须调用单个入口点;在 GNU 编译器中,入口点定义在 crt0.o 链接目标文件中,其来源是汇编程序文件 crt0.s - 在执行各种运行时启动任务(例如建立堆栈,静态初始化)。所以在构建一个链接默认crt0.o的可执行文件时,你必须有一个main(),否则你会得到一个链接器错误,因为在crt0.o中main()是一个未解析的符号。

    修改 crt0.s 以调用不同的入口点是可能的(如果有些不正当和不必要的话)。只需确保为您的项目创建这样一个目标文件,而不是修改默认版本,否则您将破坏该机器上的每个构建。

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

    【讨论】:

    • 操作系统的加载器没有调用main()
    【解决方案6】:

    main 由 glibc 调用,它是应用程序的一部分(环 3),而不是内核(环 0)。
    驱动程序有另一个入口点,例如基于 WDM 的 windows 驱动程序是从 DRIVERENTRY 开始的

    【讨论】:

      【解决方案7】:

      在机器语言中,事情是按顺序执行的,先执行的先执行。因此,编译器默认调用您的 main 方法以符合 C 标准。

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

      但是您可以编写调用任意 C 程序函数的汇编代码(实际上与调用库函数的工作方式相同),这与其他可执行文件的工作方式相同。但问题是你不能在普通的标准 C 中做到这一点,你必须求助于汇编甚至其他一些编译器特定的技巧。

      这是一个一般性和肤浅的解释,我故意避免了一些技术差异,因为它们似乎并不相关。

      【讨论】:

        猜你喜欢
        • 2018-12-09
        • 2019-08-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-30
        • 2010-10-29
        • 2012-09-08
        • 2011-06-28
        相关资源
        最近更新 更多