gcc编译C程序的过程

我们在编译c程序的时候其过程一般点击一个图标,或者执行一个命令就完成了。但实际的过程可不是这么简单的。在使用gcc编译程序时,它就把这个过程分成了四布来完成。实际上gcc是一个编译环境包含这四步的工具,这四步分别是:
(1)预处理:(Pre-Processing)
(2)编译:(Compiling)
(3)汇编:(Assembling)
(4)链接:(Linking)
使用gcc,我们可以根据需要让程序的编译停在任何的阶段。以便检查或使用编译器在该阶段输出的信息,或者对最后生成的文件进行控制,以便通过加入不同数量或种类的调试代码为今后的调试做准备。

用一个最简单的程序编译程来观察编译器工作。下面是实验的源程序hello.c。

1
2
3
4
5
6
7
#include <stdio.h>  

int main()
{

printf("Hello,world!");
return 0;
}

(1)在预处理阶段,预处理器(cpp)处理。输入的是C语言的源文件。这个阶段主要处理文件中的预处理语句,也就是通常由#开头的语句,如#include,#define,#ifdef等。这个过程会生成一个中间文件,但一般不会专门生成这种文件,所以在我们编译程序的时候不会看到类似的文件。如果必须要生成这样的文件,可以使用下面的Shell命令。

1
gcc -E hello.c -o hello.i

使用这个命令生成的是一个仍然可以用vim察看的文件,只不过由一个只有7行代码的程序变成了一个有800多行代码的程序,原因是应为在生成的hello.i文件中把include进的库的相关代码导入了。

(2)在编译阶段,编译器(ccl)处理。编译器将中间文件hello.i,编译后生成汇编语言文件*.s,使用的Shell命令是。

1
gcc -S hello.i -o hello.s

在这个过程生成的hello.s文件依然可以用vim或其他任何的文本编辑器打开。hello.s的内容如下:

1
    .file   "hello.c"  
    .section    .rodata  
.LC0:  
    .string "Hello,world!"  
    .text  
    .globl  main  
    .type   main, @function  
main:  
.LFB0:  
    .cfi_startproc  
    pushl   %ebp  
    .cfi_def_cfa_offset 8  
    .cfi_offset 5, -8  
    movl    %esp, %ebp  
    .cfi_def_cfa_register 5  
    andl    $-16, %esp  
    subl    $16, %esp  
    movl    $.LC0, (%esp)  
    call    printf  
    movl    $0, %eax  
    leave  
    .cfi_restore 5  
    .cfi_def_cfa 4, 4  
    ret  
    .cfi_endproc  
.LFE0:  
    .size   main, .-main  
    .ident  "GCC: (Debian 4.7.2-5) 4.7.2"  
    .section    .note.GNU-stack,"",@progbits 

``` 
(3)在汇编阶段,汇编器(as)将输入的hello.s转换成二进制机器代码文件*.o。这个阶段所使用的命令是。
```shell
gcc -c hello.s -o hello.o

这个阶段会生成一个二进制的文件hello.o,现在已经不可以使用vim察看了,但是还可以使用od来察看,可以看到文件内容是如下一些的数字。

1
0000000 042577 043114 000401 000001 000000 000000 000000 000000  
0000020 000001 000003 000001 000000 000000 000000 000000 000000  
0000040 000424 000000 000000 000000 000064 000000 000000 000050  
0000060 000015 000012 104525 101745 170344 166203 143420 022004  
0000100 000000 000000 176350 177777 134377 000000 000000 141711  
0000120 062510 066154 026157 067567 066162 020544 000000 041507  
0000140 035103 024040 062504 064542 067141 032040 033456 031056  
0000160 032455 020051 027064 027067 000062 000000 000024 000000  
0000200 000000 000000 075001 000122 076001 000410 006033 002004  
0000220 000610 000000 000034 000000 000034 000000 000000 000000  
0000240 000034 000000 040400 004016 001205 006502 054005 006305  
0000260 002004 000000 027000 074563 072155 061141 027000 072163  
0000300 072162 061141 027000 064163 072163 072162 061141 027000  
0000320 062562 027154 062564 072170 027000 060544 060564 027000  
0000340 071542 000163 071056 062157 072141 000141 061456 066557  
0000360 062555 072156 027000 067556 062564 043456 052516 071455  
0000400 060564 065543 027000 062562 027154 064145 063137 060562  
0000420 062555 000000 000000 000000 000000 000000 000000 000000  
0000440 000000 000000 000000 000000 000000 000000 000000 000000  
0000460 000000 000000 000000 000000 000000 000000 000037 000000  
0000500 000001 000000 000006 000000 000000 000000 000064 000000  
0000520 000034 000000 000000 000000 000000 000000 000004 000000  
0000540 000000 000000 000033 000000 000011 000000 000000 000000  
0000560 000000 000000 001744 000000 000020 000000 000013 000000  
0000600 000001 000000 000004 000000 000010 000000 000045 000000  
0000620 000001 000000 000003 000000 000000 000000 000120 000000  
0000640 000000 000000 000000 000000 000000 000000 000004 000000  
0000660 000000 000000 000053 000000 000010 000000 000003 000000  
0000700 000000 000000 000120 000000 000000 000000 000000 000000  
0000720 000000 000000 000004 000000 000000 000000 000060 000000  
0000740 000001 000000 000002 000000 000000 000000 000120 000000  
0000760 000015 000000 000000 000000 000000 000000 000001 000000  
0001000 000000 000000 000070 000000 000001 000000 000060 000000  
0001020 000000 000000 000135 000000 000035 000000 000000 000000  
0001040 000000 000000 000001 000000 000001 000000 000101 000000  
0001060 000001 000000 000000 000000 000000 000000 000172 000000  
0001100 000000 000000 000000 000000 000000 000000 000001 000000  
0001120 000000 000000 000125 000000 000001 000000 000002 000000  
0001140 000000 000000 000174 000000 000070 000000 000000 000000  
0001160 000000 000000 000004 000000 000000 000000 000121 000000  
0001200 000011 000000 000000 000000 000000 000000 001764 000000  
0001220 000010 000000 000013 000000 000010 000000 000004 000000  
0001240 000010 000000 000021 000000 000003 000000 000000 000000  
0001260 000000 000000 000264 000000 000137 000000 000000 000000  
0001300 000000 000000 000001 000000 000000 000000 000001 000000  
0001320 000002 000000 000000 000000 000000 000000 001434 000000  
0001340 000260 000000 000014 000000 000011 000000 000004 000000  
0001360 000020 000000 000011 000000 000003 000000 000000 000000  
0001400 000000 000000 001714 000000 000025 000000 000000 000000  
0001420 000000 000000 000001 000000 000000 000000 000000 000000  
0001440 000000 000000 000000 000000 000000 000000 000001 000000  
0001460 000000 000000 000000 000000 000004 177761 000000 000000  
0001500 000000 000000 000000 000000 000003 000001 000000 000000  
0001520 000000 000000 000000 000000 000003 000003 000000 000000  
0001540 000000 000000 000000 000000 000003 000004 000000 000000  
0001560 000000 000000 000000 000000 000003 000005 000000 000000  
0001600 000000 000000 000000 000000 000003 000007 000000 000000  
0001620 000000 000000 000000 000000 000003 000010 000000 000000  
0001640 000000 000000 000000 000000 000003 000006 000011 000000  
0001660 000000 000000 000034 000000 000022 000001 000016 000000  
0001700 000000 000000 000000 000000 000020 000000 064000 066145  
0001720 067554 061456 066400 064541 000156 071160 067151 063164  
0001740 000000 000000 000014 000000 002401 000000 000021 000000  
0001760 005002 000000 000040 000000 001002 000000  
0001774

(4)最后,连接器(ld)在链接阶段将输入的二进制机器代码文件*.o汇集成一个可执行的二进制代码文件。使用如下的代码。

1
gcc hello.o -o hello

最后在来使用od命令察看最后生成的hello文件,会发现它要比hello.o文件更大一些,原因在与,在最后一步的时候,把其他的二进制代码也链接进来了,如库文件等。

分享到 评论