在C中,main()方法最初是如何调用的?
C程序如何开始?
操作系统调用main()
函数。 实际上,它通常会调用其他一些被称为_init
的奇怪事物。 C编译器将标准库链接到提供此操作系统定义入口点的每个应用程序,然后调用main()
。
编辑:显然,这是不够详细和正确的一些人。
许多Unix操作系统使用的可执行和可链接格式(ELF)定义了入口点地址。 这是程序在完成其exec()
调用后开始运行的地方。 在Linux系统上,这是_init。
从objdump -d:
Disassembly of section .init: 08049f08 <_init>: 8049f08: 55 push %ebp 8049f09: 89 e5 mov %esp,%ebp 8049f0b: 83 ec 08 sub $0x8,%esp 8049f0e: e8 a1 05 00 00 call 804a4b4 <call_gmon_start> 8049f13: e8 f8 05 00 00 call 804a510 <frame_dummy> 8049f18: e8 d3 50 00 00 call 804eff0 <__do_global_ctors_aux> 8049f1d: c9 leave 8049f1e: c3 ret
来自readelf -d:
0x00000001 (NEEDED) Shared library: [libstdc++.so.6] 0x00000001 (NEEDED) Shared library: [libm.so.6] 0x00000001 (NEEDED) Shared library: [libgcc_s.so.1] 0x00000001 (NEEDED) Shared library: [libpthread.so.0] 0x00000001 (NEEDED) Shared library: [libc.so.6] 0x0000000c (INIT) 0x8049f08 0x0000000d (FINI) 0x804f018 0x00000004 (HASH) 0x8048168 0x00000005 (STRTAB) 0x8048d8c 0x00000006 (SYMTAB) 0x804867c 0x0000000a (STRSZ) 3313 (bytes) 0x0000000b (SYMENT) 16 (bytes) 0x00000015 (DEBUG) 0x0 0x00000003 (PLTGOT) 0x8059114 0x00000002 (PLTRELSZ) 688 (bytes) 0x00000014 (PLTREL) REL 0x00000017 (JMPREL) 0x8049c58 0x00000011 (REL) 0x8049be0 0x00000012 (RELSZ) 120 (bytes) 0x00000013 (RELENT) 8 (bytes) 0x6ffffffe (VERNEED) 0x8049b60 0x6fffffff (VERNEEDNUM) 3 0x6ffffff0 (VERSYM) 0x8049a7e 0x00000000 (NULL) 0x0
你可以看到INIT等于_init的地址。
frame_dummy和__do_global_ctors_aux的代码位于一组名为crtbegin.o和crtend.o(以及这些名称的变体)的文件中。 这些是GCC的一部分。 该代码为C程序做了各种必要的事情,比如设置stdin,stdout,全局variables和静态variables等。
下面的文章很好地描述了它在Linux中的作用(从下面的答案中获得较less的投票): http : //dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html
我相信别人的回答已经描述了Windows的function。
最终它是操作系统。 通常在实际入口点和主要函数之间存在一些介质,这是由编译器链接器插入的。
一些细节(与Windows有关):在PE文件中有一个叫做IMAGE_OPTIONAL_HEADER
头文件,它有一个AddressOfEntryPoint
字段,它依次是要执行的文件中第一个代码字节的地址。
操作系统调用main。 在可重定位的可执行文件中会有一个地址指向main的位置(请参阅Unix ABI了解更多信息)。
但是 ,谁称操作系统?
中央处理单元在“RESET”信号上(在上电时也被断言)将开始在给定的地址(比如0xffff)查看一些ROM中的指令。
通常情况下,会有某种跳转指令进入BIOS,configuration内存芯片,加载基本硬盘驱动程序等等。然后读取硬盘驱动器的引导扇区,然后启动下一个引导加载程序,它加载包含如何读取NTFS分区以及如何读取内核文件本身的基本信息的文件。 内核环境将被设置,内核被加载,然后 – 然后! – 内核将跳转到执行。
在完成所有这些努力之后,内核可以继续加载我们的软件。
操作系统调用包含在C运行时(CRT)中的函数并链接到可执行文件中。 称这个“CRT主”。
CRT主要做一些事情,至less在C ++中,其中最重要的两件事是运行一个全局C ++类的数组并调用它们的构造函数,并调用main()函数并将其返回值赋给shell 。
Visual C ++ CRT主要做一些事情,如内存服务。 它configuration内存分配器,重要的是如果使用debuggingCRT来帮助查找内存泄漏或访问不良。 它也在一个结构化的exception处理程序中调用main,以捕获不良的内存访问和其他崩溃并显示它们。
请注意,除了已发布的答案之外,您也可以自己调用main
。 通常这是为混淆代码保留的一个坏主意。
也许最好的信息可以在下面提到的链接http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html中find ,这是我遇到的最好的一个。