C/C++编译过程
1. 编译型语言和解释型语言
如我们所知,计算机只能接受二进制代码,这种用0/1表示的二进制代码被成为机器指令。在众多编程语言中,汇编语言与机器指令相对应,它因直接操作计算机硬件而具有很高的运行效率。
对开发者更加友好的是如C/C++、java和python等高级语言。但由于高级语言是无法直接被计算机理解的,因此需要把高级语言翻译为机器语言。
将高级语言翻译为低级语言一般可分为两类:编译和解释。
编译型语言在程序执行前有一个专门的编译过程,这个过程将程序编译为计算机可执行的二进制文件,每次运行不需要进行重新编译。但这种方式的跨平台型较差。(c/c++属于编译型语言)
解释型语言不是直接将程序语言翻译为机器语言,而是先翻译成中间代码,在运行的过程中由解释器对中间代码解释运行。解释性语言每次运行时都需要进行解释,因此效率相对较低。解释性语言的跨平台性较好。(java、python属于解释型语言)
2.C/C++编译过程
C/C++的编译过程可分为如下四步:

预编译
预编译过程的主要任务是处理源代码中的预编译指令(以"#"开头),例如#include、#define、#ifdefine等。
具体任务如下:
- 删除所有的
#define,并且展开所有的宏定义。 - 处理所有的条件预编译指令,例如
#ifdef、#elif、#else、#endif等。 - 处理所有的
#include指令,将被包含的文件插入到该指令的位置。 - 删除所有的注释
//、/* */。 - 添加行号和文件名标识,比如
#2 “hello.c” 2, 以便于调试和编译器追踪调试信息。 - 保留所有的
#pragma编译器指令。
预编译的命令如下:
gcc -E hello.c -o hello.i
或者
cpp hello.c > hello.i
将如下源代码进行预编译:
#include <stdio.h>
int main(){
printf("Hello World!");//print msg
return 0;
}
在终端下输入命令gcc -E hello.c -o hello.i后得到 hello.i文件。hello.i文件中的内容就是hello.c文件经过上述预编译过程之后的结果。由于hello.i文件内容过长,下面给出部分内容:
# 1 "hello.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "hello.c"
...
...
...
extern char *ctermid (char *__s) __attribute__ ((__nothrow__ , __leaf__));
# 912 "/usr/include/stdio.h" 3 4
extern void flockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
# 942 "/usr/include/stdio.h" 3 4
# 3 "hello.c" 2
# 4 "hello.c"
int main(){
printf("Hello World!");
return 0;
}
可以看出,hello.c文件中的注释//print msg已经被删除,且#inclued <stdio.h>中的stdio.h文件被插入到该指令的位置。
编译
编译的过程是把预编译完成之后的文件进行词法分析、语法分析、语义分析以及优化等操作,然后生成相应的汇编代码文件。
使用如下命令可将生成的hello.i文件进行编译生成汇编代码文件:
gcc -S hello.i -o hello.s
利用gcc也可以直接将hello.c文件直接进行预编译和编译生成汇编代码文件:
gcc -S hello.c -o hello.s
在终端中输入 gcc -S hello.c -o hello.s中得到的汇编代码文件如下:
.file "hello.c"
.section .rodata
.LC0:
.string "Hello World!"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $.LC0, %edi
call puts
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609"
.section .note.GNU-stack,"",@progbits
汇编
汇编过程是将汇编代码转变为计算机可以执行的机器指令。汇编的过程比较简单,只需要将指令逐条翻译为机器指令即可。
下面的gcc命令可以将hello.s文件进行汇编得到hello.o文件(即目标文件)。
gcc -c hello.s -o hello.o
或者利用gcc直接对hello.c文件进行预编译、编译、汇编三个步骤得到hello.o文件。
gcc -c hello.c -o hello.o
在终端中输入gcc -c hello.c -o hello.o得到hello.o目标文件。
由于经过汇编的文件已经是二进制文件,我们无法识别出含义,这里就不给出文件内容了,有兴趣的可以试试看。
链接
经过预编译、编译、汇编后得到的hello.o文件实际上是一个无法直接运行的中间目标文件,简称目标文件。
链接是一个很复杂的过程,要得到可执行文件需要将许多文件链接起来。它的主要目的是正确处理各个模块之间的引用,使得各个模块之间能够正确的衔接。链接过程主要包括了地址和空间分配、符号决议和重定位等步骤。
下面的gcc指令将hello.o文件进行链接,得到可执行文件:
gcc hello.o -o hello
或者直接将hello.c文件进行预编译、编译、汇编、链接得到可执行文件:
gcc hello.c -o hello
在终端中输入gcc hello.c -o hello得到可执行文件,然后输入./hello执行程序。
WavenZ@pc:~$ cd Desktop
WavenZ@pc:~/Desktop$ gcc hello.c -o hello
WavenZ@pc:~/Desktop$ ./hello
Hello World!
1768

被折叠的 条评论
为什么被折叠?



