用IDE直接把源码生成可执行文件,这个过程叫构建(Build)。
实际上需要分四个步骤:预处理、编译、汇编、链接。

一、预处理。也叫预编译,编译之前的一个步骤。
先把用的示例代码列一下,输出比hello world更简单的:ok.c
[code]
#include <stdio.h>

int main(){
printf("OK!\n");
return 0;
}
[/code]
预编译指令:
[code]
gcc -E ok.c -o ok.i
[/code]
预处理的结果有16K多,非常长,内容我就不贴了。

[code]
[[email protected] x]# ll ok.*
-rw-r--r-- 1 root root 63 Jun 17 17:22 ok.c
-rw-r--r-- 1 root root 16714 Jun 17 17:22 ok.i
[/code]
这些内容,除了最后四行代码外,全是include展开的结果。实际上真正有用的只有一行。
[code]
extern int printf (__const char *__restrict __format, ...);
[/code]
把ok.c改一下
[code]
extern int printf (__const char *__restrict __format, ...);

int main(){
printf("OK!\n");
return 0;
}
[/code]
预编译生成的文件非常小,除了前四行是固定套路外,剩下的就是我们的代码了
[code]
# 1 "ok.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "ok.c"
extern int printf (__const char *__restrict __format, ...);

int main(){
printf("OK!\n");
return 0;
}
[/code]
问题:头文件的作用是什么?
目前答案:有一种错觉,头文件跟同名的.c文件是一对,编译的时候会自动匹配。上面的代码已经证实这种想法是错误的。头文件只是方法、宏之类的定义。头文件和具体实现的文件同名也只是一种习惯,而非强制性约束。

二、编译,Compilation:把预生成的代码经过词法、语法、语义分析及优化后,生成汇编代码。
编译命令如下
[code]
gcc -S ok.i -o ok.s//预编译生成的代码扩展名必须用.i,否则gcc无法识别。
gcc -S ok.c -o ok.s
[/code]
用两种方式得到的.i文件,编译后的结果相同。
生成的汇编代码如下。
[code]
.file "ok.c"
.section .rodata
.LC0:
.string "OK!"
.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
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-3)"
.section .note.GNU-stack,"",@progbits
[/code]
编译的几个步骤:
[Source Code]--Scanner-->[Tokens]--Parser-->[Syntax Tree]--Semantic Analyzer-->[Commented Syntax Tree]--Source Code Optimizer-->[Intermediate Representation]--Code Generator-->[Target Code]--Code Optimizer-->[Final Target Code]

[源码]--扫描器-->[标记]--语法分析器-->[语法树]--语义分析器-->[注释语法树]--源码级优化器-->[中间代码]--代码生成器-->[目标代码]--目标代码优化器-->[最终目标代码]

有限状态机、上下文无关语法。BC、TC由左开始及从右开始。

问题:为什么变量不能以数字开始?
目前答案:为了在词法分析时,方便区分数字常量和变量。比如:int 1 = 2;int x = 1 + 2;就乱套了。或者:long 1L = 2L;long x = 1L + 2L。

三、汇编:把汇编代码转成机器指令。
[code]
gcc -c ok.s -o ok.o
gcc -c ok.c -o ok.o
[/code]

四、链接:把一些指令对其他符号地址的引用加以修正。包括地址和空间分配、符号决议、向定位。
问题:有地址分配可以理解,为什么有空间分配?

可能的答案:把所有的符号表收集起来,放到一个全局符号表中,这个全局符号表需要空间存储。将各个.o文件和成一个文件,同样需要存储。

1、主要任务把所有的符号表收集起来,放到一个全局符号表中。
2、将各个.o文件合成一个文件。
3、链接器计算出符号地址。
4、符号位置修正。

标签: gcc, 编译, 链接

添加新评论