Skypx 观心知天下, 不露亦锋芒

C 语言的编译过程


C语言的真正编译过程

我们都知道无论是用C或者C++来开发程序,用各种IDE来开发即省时又省力 因为IDE已经帮我们做好了编译过程,隐藏了很多细节. 以至于我们对整个过程一无所知.今天我们就来看下编译的过程.

C语言的编译大致分为四部:

  1. 预处理
  2. 把源代码翻译成汇编语言
  3. 把汇编语言翻译成二进制文件
  4. 链接

我们分部来看下每一步都做了什么

准备源码文件 hello.c

#include <stdio.h>
#include <stdlib.h>
int main()
{
        printf("hello world!\n");
        return 0;
}

1. 预处理

gcc -E hello.c -o a.c可以生成预处理后的文件。

通过查看文件内容和文件大小可以得知a.c讲stdio.h和stdlib.h包含了进来
预处理过程实质上是处理“#”,将#include包含的头文件直接拷贝到hello.c当中;将#define定义的宏进行替换,同时将代码中没用的注释部分删除等

具体做的事儿如下:

(1)将所有的#define删除,并且展开所有的宏定义。说白了就是字符替换
(2)处理所有的条件编译指令,#ifdef #ifndef #endif等,就是带#的那些
(3)处理#include,将#include指向的文件插入到该行处
(4)删除所有注释
(5)添加行号和文件标示,这样的在调试和编译出错的时候才知道是是哪个文件的哪一行
(6)保留#pragma编译器指令,因为编译器需要使用它们.(#pragma once 防止重复引用头文件)

可以查看生成的a.c文件,把所有的include的文件插入进指定的位置. 所有宏都已经展开

2. 把源代码翻译成汇编语言

gcc -S hello.c -o a.s可以生成汇编代码

(1)词法分析,

(2)语法分析

(3)语义分析

(4)优化后生成相应的汇编代码


 1         .file   "hello.c"
 2         .section        .rodata
 3 .LC0:
 4         .string "hello world!"
 5         .text
 6         .globl  main
 7         .type   main, @function
 8 main:
 9 .LFB0:
10         .cfi_startproc
11         pushl   %ebp
12         .cfi_def_cfa_offset 8
13         .cfi_offset 5, -8
14         movl    %esp, %ebp
15         .cfi_def_cfa_register 5
16         andl    $-16, %esp
17         subl    $16, %esp
18         movl    $.LC0, (%esp)
19         call    puts
20         movl    $0, %eax
21         leave
22         .cfi_restore 5
23         .cfi_def_cfa 4, 4
24         ret
25         .cfi_endproc
26 .LFE0:
27         .size   main, .-main
28         .ident  "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
29         .section        .note.GNU-stack,"",@progbits

3. 把汇编语言翻译成二进制文件

gcc -c hello.c -o a.o 类Uinx系统编译的结果生生成.o文件,Windows系统是生成.obj文件

计算机只识别二进制文件

4. 链接

gcc hello.c -o main 直接输出文件

就像刚才的hello.c它使用到了C标准库的东西“printf”,但是编译过程只是把源文件翻译成二进制而已,这个二进制还不能直接执行,这个时候就需要做一个动作,将翻译成的二进制与需要用到库绑定在一块

举个例子,像printf函数是系统定义的函数,有系统库,我们只是做了一个动作,把库文件和我们的文件做了一个链接: 可以通过ldd的指令查看

pxwen@pxwen-Think:~/Workstation/cmake/studycmake$ ldd main
	linux-vdso.so.1 =>  (0x00007ffcacb9d000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd1e3828000)
	/lib64/ld-linux-x86-64.so.2 (0x000055931be8f000)

可以看到是和系统下的库做了一个链接

总结

其实我们直接可以gcc hello.c 就可以输出一个在Linux下的可执行文件, 我只是把这个过程做了一个分步,希望大家看的更清楚一点. gcc hello.c 其实是把这四个步骤合并了,希望对大家有点帮助。


评论

Content