序
一个编译过程包括下面4个阶段
- 预处理,预处理器cpp主要进行3个方面:文件包含、宏定义、条件编译;
- 编译,gcc将c文件编译成汇编文件;
- 汇编,as将汇编文件编译成机器码;
- 链接,ld将目标文件和外部符号进行链接,得到一个可执行二进制文件。
下面以一个简单的test.c来探讨这个过程
#define number (1 2) int main(void) { int num = number; return 0; }
1、预处理
预处理主要做下列的处理
- 将所有的#define删除,并且展开所有的宏定义
- 处理所有的条件预编译指令,比如#if #ifdef #elif #else #endif等
- 处理#include 预编译指令,将被包含的文件插入到该预编译指令的位置。
- 删除所有注释 “//”和”/* */”.
- 添加行号和文件标识,以便编译时产生调试用的行号及编译错误警告行号。
- 保留所有的#pragma编译器指令,因为编译器需要使用它们
[root@localhost test]# gcc -e test.c > test.i //等价于 cpp test.c >test.i [root@localhost test]# cat test.i # 1 "test.c" # 1 "" # 1 " " # 1 "test.c" //使用gcc -e -p test.c > test.i 就不会显示上面4行#号开头的内容 int main(void) { int num = (1 2); //这里进行了行宏替换 return 0; }
2、编译
预处理后的代码生成汇编代码.
编译过程可分为6步:扫描(词法分析)、语法分析、语义分析、源代码优化、代码生成、目标代码优化。
- 词法分析:扫描器(scanner)将源代码的字符序列分割成一系列的记号(token)。lex工具可实现词法扫描。
- 语法分析:语法分析器将记号(token)产生语法树(syntax tree)。yacc工具可实现语法分析。
- 语义分析:静态语义(在编译器可以确定的语义)、动态语义(只能在运行期才能确定的语义)。
- 源代码优化:源代码优化器(source code optimizer),将整个语法书转化为中间代码(intermediate code)(中间代码是与目标机器和运行环境无关的)。中间代码使得编译器被分为前端和后端。编译器前端负责产生机器无关的中间代码;编译器后端将中间代码转化为目标机器代码。
- 目标代码生成:代码生成器(code generator).
- 目标代码优化:目标代码优化器(target code optimizer)。
[root@localhost test]# gcc -s test.i > test.s [root@localhost test]# cat test.s .file "test.c" .text .globl main .type main, @function main: pushl �p movl %esp, �p subl $16, %esp movl $3, -4(�p) movl $0, �x leave ret .size main, .-main .ident "gcc: (gnu) 4.4.7 20120313 (red hat 4.4.7-4)" .section .note.gnu-stack,"",@progbits
3、汇编
将汇编文件编译成机器码。
[root@localhost test]# gcc -c test.s -o test.o //等价于 as test.s -o test.o
4、链接
通过调用链接器来链接程序运行需要的一大堆目标文件,以及所依赖的其它库文件,最后生成可执行文件。
链接的主要内容是把各个模块之间相互引用的部分处理好,使得各个模块之间能够正确地衔接。
链接的主要过程包括:地址和空间分配(address and storage allocation),符号决议(symbol resolution),重定位(relocation) 等。
链接分为静态链接和动态链接。
静态链接是指在编译阶段直接把静态库加入到可执行文件中去,这样可执行文件会比较大。
而动态链接则是指链接阶段仅仅只加入一些描述信息,而程序执行时再从系统中把相应动态库加载到内存中去。
[root@localhost test]# gcc test.o -o test
gcc编译的详细过程
[root@localhost swap]# gcc -v test.c using built-in specs. target: i686-redhat-linux configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-languages=c,c ,objc,obj-c ,java,fortran,ada --enable-java-awt=gtk --disable-dssi --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libjava-multilib --with-ppl --with-cloog --with-tune=generic --with-arch=i686 --build=i686-redhat-linux thread model: posix gcc version 4.4.7 20120313 (red hat 4.4.7-4) (gcc) collect_gcc_options='-v' '-mtune=generic' '-march=i686' /usr/libexec/gcc/i686-redhat-linux/4.4.7/cc1 -quiet -v test.c -quiet -dumpbase test.c -mtune=generic -march=i686 -auxbase test -version -o /tmp/ccaffwbo.s ignoring nonexistent directory "/usr/lib/gcc/i686-redhat-linux/4.4.7/include-fixed" ignoring nonexistent directory "/usr/lib/gcc/i686-redhat-linux/4.4.7/../../../../i686-redhat-linux/include" #include "..." search starts here: #include <...> search starts here: /usr/local/include /usr/lib/gcc/i686-redhat-linux/4.4.7/include /usr/include end of search list. gnu c (gcc) version 4.4.7 20120313 (red hat 4.4.7-4) (i686-redhat-linux) compiled by gnu c version 4.4.7 20120313 (red hat 4.4.7-4), gmp version 4.3.1, mpfr version 2.4.1. ggc heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 compiler executable checksum: 5f02f32570d532de29ae0b402446343a collect_gcc_options='-v' '-mtune=generic' '-march=i686' as -v -qy -o /tmp/ccvfdr55.o /tmp/ccaffwbo.s gnu assembler version 2.20.51.0.2 (i686-redhat-linux) using bfd version version 2.20.51.0.2-5.34.el6 20100205 compiler_path=/usr/libexec/gcc/i686-redhat-linux/4.4.7/:/usr/libexec/gcc/i686-redhat-linux/4.4.7/:/usr/libexec/gcc/i686-redhat-linux/:/usr/lib/gcc/i686-redhat-linux/4.4.7/:/usr/lib/gcc/i686-redhat-linux/:/usr/libexec/gcc/i686-redhat-linux/4.4.7/:/usr/libexec/gcc/i686-redhat-linux/:/usr/lib/gcc/i686-redhat-linux/4.4.7/:/usr/lib/gcc/i686-redhat-linux/ library_path=/usr/lib/gcc/i686-redhat-linux/4.4.7/:/usr/lib/gcc/i686-redhat-linux/4.4.7/:/usr/lib/gcc/i686-redhat-linux/4.4.7/../../../:/lib/:/usr/lib/ collect_gcc_options='-v' '-mtune=generic' '-march=i686' /usr/libexec/gcc/i686-redhat-linux/4.4.7/collect2 --eh-frame-hdr --build-id -m elf_i386 --hash-style=gnu -dynamic-linker /lib/ld-linux.so.2 /usr/lib/gcc/i686-redhat-linux/4.4.7/../../../crt1.o /usr/lib/gcc/i686-redhat-linux/4.4.7/../../../crti.o /usr/lib/gcc/i686-redhat-linux/4.4.7/crtbegin.o -l/usr/lib/gcc/i686-redhat-linux/4.4.7 -l/usr/lib/gcc/i686-redhat-linux/4.4.7 -l/usr/lib/gcc/i686-redhat-linux/4.4.7/../../.. /tmp/ccvfdr55.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i686-redhat-linux/4.4.7/crtend.o /usr/lib/gcc/i686-redhat-linux/4.4.7/../../../crtn.o