程序员的自我修养: 编译器工作过程
2010-03-04 23:17
281 查看
//hello.c #include <stdio.h> int main() { printf("Hello world/n"); return 0; }
hello.c的编译过程如下:
Source Code(hello.c) Header Files(Stdio.h) - 预处理(Prepressing) -> hello.i - 编译(Compilation) -> hello.s -汇编(Assembly) (汇编器是将汇编代码转变成机器可执行的机器指令) -> hello.o -> 链接(Linking) -> a.out Static Library
hello.i的部分内容如下:
# 1 "/usr/include/bits/sys_errlist.h" 1 3 4 # 27 "/usr/include/bits/sys_errlist.h" 3 4 extern int sys_nerr; extern __const char *__const sys_errlist[]; # 827 "/usr/include/stdio.h" 2 3 4 extern int fileno (FILE *__stream) __attribute__ ((__nothrow__)) ; extern int fileno_unlocked (FILE *__stream) __attribute__ ((__nothrow__)) ; # 846 "/usr/include/stdio.h" 3 4 extern FILE *popen (__const char *__command, __const char *__modes) ; extern int pclose (FILE *__stream); extern char *ctermid (char *__s) __attribute__ ((__nothrow__)); # 886 "/usr/include/stdio.h" 3 4 extern void flockfile (FILE *__stream) __attribute__ ((__nothrow__)); extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__)) ; extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__)); # 916 "/usr/include/stdio.h" 3 4 # 2 "hello.c" 2 int main() { int i = 0; i = (2>3?2:3); printf("Hello world/n"); return 0; }
hello.s的内容如下:
.file "hello.c" .section .rodata .LC0: .string "Hello world" .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp andl $-16, %esp subl $32, %esp movl $0, 28(%esp) movl $3, 28(%esp) movl $.LC0, (%esp) call puts movl $0, %eax leave ret .size main, .-main .ident "GCC: (Ubuntu 4.4.1-4ubuntu9) 4.4.1" .section .note.GNU-stack,"",@progbits
总的来说, 过程如下:
预编译(生成hello.i),编译(生成hello.s),汇编(生成hello.o),链接(生成a.out)
1. 编译的过程又有如下过程:
源代码 - 词法分析(Scanner)->Tokens - 语法分析(Paraser) -> Syntax Tree - 语义分析器(Semantic Analyzer) -> Commented Syntax Tree - 源代码级优化器(Source Code Optimizer)->Intermediate Representation - 代码生成器(Code Generator)->Target Code - 目标代码优化器(Code Optimizer)->Final Target Code
2. 链接的过程如下:
Library Object File a.o -Link->a.out Object File b.o
链接时,假如我们在程序模块main.c中使用另一个模块服从func.c中的函数foo(), 在编译时, main.c假设foo的地址是0,当连接时,main.c才在func.c中查找foo()的地址。