该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
c语言为什么需要编译链接
朱有鹏
1、C语言为什么需要编译链接
1.1、编译链接的流程
(1)编译流程图
(2)编译链接实例
由源码到可执行程序的整个过程:以我们再熟悉不过的hello.c为例
Hello.c
#include
int main(int argc, char *argv[])
{
printf("hello world.\n");
return 0;
}
对于这个c语言程序,我们必须经过一定手段,将其编程二进制可执行文件,然后由系统加载执行。在Linux系统中,gcc编译时,gcc编译程序会读取源代码hello.c文件,并且将其翻译成一个可执行文件hello,整个过程共四个阶段,由编译工具链完成,在这里我们来完整的看下对hello.c进行编译链接的四个过程。
第一过程:预处理(cpp),在命令行下输入gcc -E hello.c -o hello.i(gcc预处理)的命令,预处理器会对以#开头的预处理命令进行处理。譬如hello.c中的#include,预处理器会将系统中的hello.h的具体内容读取到文本中,替换原有的#include。得到一个新的C程序,我们一般称为.i文件,这里得到的hello.i文件格式如下:
Hello.i
...
...
extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
# 943 "/usr/include/stdio.h" 3 4
# 2 "hello.c" 2
int main(int argc, char *argv[])
{
printf("hello world.\n");
return 0;
}
第二过程:编译(cc),在命令行下输入:gcc -S hello.i -o hello.s(gcc编译),当然也可以gcc -S hello.c -o hello.s,只是这种方式是由预处理器和编译器一起完成的,编译器将hello.i翻译成了hello.s汇编文件,汇编程序是一条条通用的机器语言指令。
hello.s
.file "hello.c"
.section .rodata
.LC0:
.string "hello world."
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $16, %esp
movl $.LC0, (%esp)
call puts
movl $0, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size main, .-main
...
第三过程:汇编(as),gcc -c hello.s -o hello.o(gcc汇编),汇编器会将hello.s翻译成机器语言指令,将这些指令打包成为***.o格式的可重定位文件,并将结果保存在目标文件hello.o中,目标文件是由不同的段组成,通常一个目标至少有两个段,数据段和代码段。hello.o用文本文档打开后是无法被看懂的,因为这是二进制文件。
第四过程:链接(ld),gcc hello.o -o hello.out(gcc链接),链接是最后一个过程,链接器会将hello.o和其它库文件,其它目标代码链接后形成可执行文件,在本程序中hello.c里调用了printf函数,链接器会将printf.o文件并入到我们的hello.out可执行文件中。最后将可执行文件加载到存储器后,然后由系统执行。每一个阶段产生的文件都是不同的,可以在Linux命令行下查看这四个文件的结果如下:
ls -l hello*
-rw-r--r-- 1 root root 97 Mar 8 01:31 hello.c
-rw-r--r-- 1 root root 17580 Mar 8 01:32 hello.i
-rw-r--r-- 1 root root 485 Mar 8 01:32 hello.s
-rw-r--r-- 1 root root 1024 Mar 8 01:32 hello.o
-rwxr-xr-x 1 root root 7292 Mar 8 01:33 hello.out
我们平时所说的编译器实质是指是编译工具链,预处理用预处理器(preprocessor) ,汇编用汇编器,链接用链接器,这几个工具再加上其他一些额外会用到的工具,合起来叫编译工具链。gcc就是一个编译工具链。
1.2、编译连接中各种文件扩展名的含义
在Linux系统中,分为可执行文件和不可执行文件,由源码到可执行程序的过程中,以扩展名(即后缀)来区分各个阶段。gcc中一些常见的扩展名,我们需要注意扩展名的写法及其背后的含义,否则编译失败。例如:后缀为.s和.S文件的区别?
gcc中一些常见的扩展名
扩展名 含义 扩展名 含义
.c C语言源代码文件 .m Objective-C源代码文件
.a 由目标文件构成的静态库文件 .o 编译后的目标文件
.C C++源代码文件 .out 链接器生成的可执行文件
.h 程序所包含的头文件 .s 汇编语言源代码文件
.i 预处理过的C源代码文件 .S 经过预处理、编译和汇编后的汇编源代码文件
.ii 预处理过的C++源代码文件