当我们进行编译的时候,要使用一系列的工具,我们称之为工具链,其中包括:
预处理器cpp
编译器gcc/g++
汇编器as
链接器ld

一个C/C++程序编译过程包括下面几个阶段:

预处理    预处理器cpp将对源文件中的宏进行展开。
编译     gcc将c文件编译成汇编文件。
汇编     汇编器as将汇编文件编译成机器码。
链接     链接器ld将目标文件和外部符号进行连接,得到一个可执行二进制文件。

下面以一个很简单的 hello.c 来探讨这个过程
......
 #include <stdio.h>
#define BUFSIZE 1024

int main(int argc, char *argv[])
{
    char hello[BUFSIZE] = "Hello my friend!";
    printf("%s\n", hello);
    return 0;
}
 ......

### 预处理(预处理器 cpp)

[butbueatiful@xt myhello]$ gcc -E hello.c -o hello.i

[butbueatiful@xt myhello]$ cpp hello.c -o hello.i

我们用vi打开 hello.i 可以看到有如下内容:
......
int main(int argc, char *argv[])
{
    char hello[1024] = "Hello my friend!";
    printf("%s\n", hello);
    return 0;
}

......
我们可以看到,文件中宏定义 BUFSIZE 出现的位置被 1024 替换掉了,其它的内容保持不变。

### 编译器将 .i 文件编译成汇编文件

[butbueatiful@xt myhello]$ gcc -S hello.i # 得到汇编文件hello.s

### 汇编器将汇编文件编译成机器码(汇编器 as)
[butbueatiful@xt myhello]$ gcc -c hello.s -o hello.o

[butbueatiful@xt myhello]$ as hello.s -o hello.o

hello.o中为目标机器上的二进制文件

用 nm 查看文件中的符号:
[butbueatiful@xt myhello]$ nm -a hello.o
00000000 b .bss
00000000 n .comment
00000000 d .data
00000000 n .note.GNU-stack
00000000 t .text
00000000 a hello.c
00000000 T main
U puts
既然已经是二进制目标文件了,能不能执行呢?
[butbueatiful@xt myhello]$ chmod +x hello.o
[butbueatiful@xt myhello]$ ./hello.o
-bash: ./hello.o: cannot execute binary file
其实这时 puts 前面的 U 表示这个符号的地址还没有定下来,T表示这个符号属于代码段。ld连接的时候会为这些带U的符号确定地址。

### 链接(链接器 ld)
链接需要指定库的位置。通常程序中会有很多的外部符号,因此需要指定的位置就会很多。
不过,我们只需要调用 gcc 即可,ld会自己去找这些库的位置。
[butbueatiful@xt myhello]$ gcc hello.o -o hello # 得到可执行文件hello