对于一个 C 程序,从源文件到形成可执行程序一共要进行四步:预处理、编译、汇编、链接 。
接下来,我们用 gcc 分别演示这四个过程。
一、预处理(进行宏替换)
预处理中,需要完成头文件的展开、宏替换、去注释、条件编译等工作。
cpp
gcc -E file.c -o file.i
-E 选项:让 gcc 进行程序的预处理,预处理之后就停下来。
-o 选项:-o 选项紧跟目标生成文件的名称。gcc -o file.i file.c 或者上方的指令都是可以的。
可以发现 头文件 #include <stdio.h> 被展开,宏被替换,注释被删除
二、编译
当程序在编译时,gcc 会检查代码是否有语法错误,了解代码基本内容,检查无误就会把代码翻译为汇编语言。
gcc -S file.i -o file.s
-S 选项:让 gcc 进行程序的编译,编译之后就停下来。
我们也可以接着从上次预编译后的文件继续执行。
三、汇编
汇编之后会把汇编语言转换为二进制文件。
由于机器只认识二进制文件,所以这一步就是把人能看懂的文件,翻译成机器能看懂的,就是 生成机器可识别代码。
gcc -c file.s -o test.o
-c 选项:让 gcc 进行程序的汇编,汇编之后就停下来。
注意: 二进制文件不可执行
四、链接
链接过程就是将程序和对应的库链接起来,编译器会自动识别语言。
gcc file.o -o myfile
生成可执行文件,完成链接
五、动静态链接库
1.什么是库
首先清楚一点,我们写的代码里面经常会用库函数,这些调用接口的方式是我们写的,但是这些库函数的底层实现不是我们写的,而这中间就有一个调用库的过程。
我们的C程序中,并没有定义"printf"的函数实现,且在预编译中包含的"stdio.h"中也只有该函数的声明,而 没有定义函数的实现,那么,是在哪里实"printf"函数的呢?
最后的答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到 系统默认的搜索路径"/usr/lib"下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函 数"printf"了,而这也就是链接的作用
在上面我们写的my代码就用到了库,我们可以用ldd指令来查看,该程序都用了哪些库
在 /usr/include/ 中我们就能看到系统的内置的头文件,我们通过头文件就可以根据库函数,找到对应的方法,从而链接到正确的库:
静态库:lib....a....
动态库: lib....so....
我们的可执行程序,包括指令,都依赖于库。
2.库的链接方式
库的连接方式一共有两种:动态链接(默认)和静态链接
用 file 指令查看 test 文件的类型:
3.动态库与静态库
动态库:
动态库也可以说是共享库。动态库在链接的时候,并没有把相应的库文件加载到可执行程序中,而是在运行的时候,拷贝库中所需代码的地址到可执行程序中相关的位置。
通过这种链接方式,使用动态库就大大节省了内存损耗。
但是这也带来一个缺点,由于动态库链接时,是通过地址链接 的。所以只要我们的动态库缺失,程序便无法运行
静态库:
静态库则是在编译链接时,把库文件的代码 全部加载到可执行程序中。
这种链接方式导致生成文件体积庞大 。若文件数目一多,到时候内存就会被占用很多。
但是这也有一个优点,这样就使得程序不依赖于库了。即使库出了问题,也没事,因为我们已经将代码加载到程序中了。
抽象一点的比喻就是,我是一个学生想使用电脑,动态库是网吧,我们可以去网吧使用电脑但电脑是公共的不能带走,静态库就是自己买了一台电脑自己随意使用。
一般只默认装了动态库,我们需要自己装静态库
C语言:
yum install -y glibc-static
C++:
yum install -y libstdc++-static
可以看到使用了静态库的程序非常大
文件类型也变成静态链接
4.debug和release
1、debug版本:程序本身会被加入更多的调试信息,以便于进行调试。
2、release版本:不会添加任何调试信息,是不可调试的。
在我们平时生成的默认程序是 release版本是不可调试的,生成debug版本需要加上-g选项
debug版本包含了更多的调试信息
5.gcc/g++ 选项汇总
-E :只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面
-S :编译到汇编语言不进行汇编和链接
-c :编译到目标代码
-o :文件输出到文件
-static :此选项对生成的文件采用静态链接
-g :生成调试信息。GNU 调试器可利用该信息
-shared :此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库
-O0 :不做任何优化,这是默认的编译选项。
-O1 : 对程序做部分编译优化
-O2 :是比O1更高级的选项,进行更多的优化
-O3 :比O2更进一步的进行优化
-w :不生成任何警告信息
-Wall: 生成所有警告信息
g++安装
yum install gcc-c++
c++的代码需要用g++编译