《2022年C的编译过程 .pdf》由会员分享,可在线阅读,更多相关《2022年C的编译过程 .pdf(4页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、C 的编译过程2008 年 03 月 11 日 星期二09:14 P.M. C 程序编译过程2007-08-22 00:54 E:现在我们看一下C 的编译过程 . 预编译,编译生成汇编,汇编生成目标文件,目标文件连接库文件生成可执行文件,这个过程人人都知道,但是究竟如何呢?牢骚一下:太多的编译器都是一步到位,其实这对学习不是什么好事情,至少我觉得,在学习过程中把简单的东西弄的麻烦,在工作的过程中把麻烦的东西弄的简单。这里的例子选自 lorne 预编译 gcc -E 编译 gcc -S 这两个有 gcc rpm 包就可以。汇编 as 连接 ld 这两个需要安装binutils rpm包1,预编译
2、编写 c 源程序 game.c #include int main() printf(Hello World!n); 传说中的 helloworld gcc -E -o pregame game.c 这是会出现一个pregame 文件看看他是什么格式file pregame pregame: ASCII C program text 也就说预编译之后的文件仍然是c 源代码。那么预编译作了些什么呢?可以看看都有些什么玩意. cat pregame 你会发现 pregame 和 game 差不多,区别是pregame 中没有了 #include也灭有类似的格式。也就是说预编译的任务是处理#开头的指
3、令 ,比如拷贝 #include 包含的文件代码, #define 宏定义的替换 ,条件编译等这就是预编译的作用他把game.c 中包含的头文件加在main 函数的上面 .gcc -o game game.c 生成 可执行程序 game gcc -o pregame pregame.c 生成可执行程序pregame 注意:这两个命令,我们忽略了as 和 ld 的步骤。pregame 最好用 .c 做为文件名发现 game 和 pregame 的运行结果一样,这也说明了预编译的作用. 总结一下预编译的作用:( 这些是 lorne大人总结的,与我无关) 1. 把include的文件拷贝到要编译的源
4、文件中。2. 用实际值替代 define的文本。3. 在调用宏的地方进行宏替换。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 1 页,共 4 页 - - - - - - - - - 2. 编译。这个过程是用于生成汇编语言。这是编译过程的一步,是一步,是一步啊,同志们! gcc -S -o aspregame pregame.c 会声称一个 aspregame 文件,看看是什么东西? file aspregame aspregame: ASCII assembler program
5、text 嘿嘿出现了,汇编程序文本. 有兴趣你可以看一下cat aspregame .file pregame.c .section .rodata .LC0: .string Hello World! .text .globl main .type main, function main: leal 4(%esp), %ecx andl $-16, %esp pushl -4(%ecx) pushl %ebp movl %esp, %ebp pushl %ecx subl $4, %esp movl $.LC0, (%esp) call puts addl $4, %esp popl %ec
6、x popl %ebp leal -4(%ecx), %esp ret .size main, .-main .ident GCC: (GNU) 4.1.0 20060304 (Red Hat 4.1.0-3) .section .note.GNU-stack,progbits 传说中的汇编。最好用 .asm 作为文件名。3. 生成目标文件 . 这一步我们可以用gcc 来完成,但是还是用as 吧,总觉得一步到位的东西,不是很适合学习。as -o ldaspregame aspregame 会生成 ldaspregame 文件看看他是什么?file ldaspregame ldaspregame
7、: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 2 页,共 4 页 - - - - - - - - - ELF格式的文件,浮动的,意思是这个程序的地址是可以被修改来适应初始地址,其实就是未连接的目标文件。注意:我们可以把任何的一个.c 文件通过gcc -S 变成汇编,在把他变成目标文件。最要用 .o 做为文件名4. 连接成为可执行文件那么
8、现在我们来生成可执行文件吧这里要说明什么是动态链接库,相当于winxxx 中的 dll文件,在这里是so 文件, ELF格式就是支持动态链接库的。gcc -o exldaspregame ldaspregame 好了声称了可执行文件ldaspregame 任何一个 .c 文件都可以生成目标文件,他们需要被连接来执行预编译预编译又称为预处理,是做些代码文本的替换工作处理 #开头的指令,比如拷贝 #include包含的文件代码,#define宏定义的替换,条件编译等就是为编译做的预备工作的阶段主要处理#开始的预编译指令预编译指令指示了在程序正式编译前就由编译器 进行的操作,可以放在程序中的任何位置
9、。常见的预编译指令有:(1)#include 指令该指令指示编译器将xxx.xxx文件的全部内容插入此处。若用 括起文件则在系统的INCLUDE目录中寻找文件,若用 括起文件则在当前目录中寻找文件。一般来说, 该文件是后缀名为h 或cpp的头文件。注意: 不会在当前目录下搜索头文件,如果我们不用 而用 把头文件名扩起,其意义为在先在当前目录下搜索头文件,再在系统默认目录下搜索。(2)#define指令该指令有三种用法: 第一种是定义标识,标识有效范围为整个程序,形如#define XXX,常与 #if 配合使用;第二种是定义常数,如 #define max 100,则 max 代表 100 (
10、这种情况下使用const 定义常数更好,原因见注1);第三种是定义函数 ,如 #define get_max(a, b) (a)(b)?(a):(b) 则以后使用get_max(x,y)就可以得到x 和 y 中较大的数(这种方法存在一些弊病,见注2)。(3)#if 、#else 和 #endif指令这些指令一般这样配合使用:#if defined(标识 ) / 如果定义了标识要执行的指令#else 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 3 页,共 4 页 - - - - -
11、 - - - - 要执行的指令#endif 在头文件中为了避免重复调用(比如说两个头文件互相包含对方),常采用这样的结构:#if !(defined XXX) /XXX为一个在你的程序中唯一的标识符 ,/ 每个头文件的标识符都不应相同。/ 起标识符的常见方法是若头文件名为abc.h / 则标识为 abc_h #define XXX 真正的内容,如函数声明之类#endif 注 1:因为: const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误(边际效应)。注 2:例如get_max(a+, b)时, a+会被执行多少次取决于a 和 b 的大小!所以建议还是用内联函数而不是这种方法提高速度。虽然有这样的弊病,但这种方法的确非常灵活,因为a 和 b 可以是各种数据类型。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 4 页,共 4 页 - - - - - - - - -
限制150内