目录
前言
本章主要带着大家一起学习Linux下编译C/C++的工具,以及关于动静态库的一些基础知识;
一、程序的翻译过程
我们的C语言和C++编写出的程序属于翻译型程序,此处以C语言为例,分析程序翻译过程;
1、预编译
当我们写完一个C语言程序时,首先,我们需要进行预编译操作;预编译主要将头文件展开、宏替换、条件编译与去注释;我们可使用下列指令生成我们的预编译完的文件(以.i为结尾的文件后缀);
gcc -o 生成文件名.i -E 被预编译的文件名.c
上图为我们编写的源程序,我们来验证我们的预编译阶段是否完成了上述工作;我们执行以下执行,生成预编译完成后的文件 test.i ;
预编译生成的文件从大小上,明显可以看出比我们的源文件要大很多,我们再使用vim查看我们的test.i文件,如下图;
我们发现我们的文件一下就变成了八百多行,前面增加的便是我们的stdio文件展开后的样子,故我们test.i文件变大了很多,因为把我们的库文件代码复制到预编译后的文件中了,而且我们不难发现我们之前些的注释被去掉了,我们的宏也完成了替换,也进行了条件编译的处理,与我们的预想完全一致;
2、编译
这一步主要实现的将我们的C语言代码编译成汇编代码,我们生成的汇编代码文件以s为后缀名,具体指令如下;
gcc -o 编译生成后文件名.s -S .c文件或.i文件都可
学过汇编语言的友友们,应该就很熟悉了,这就是我们的汇编代码,编译阶段也如我们所料完成了自己的工作;
3、汇编
这一步主要是将我们的汇编代码转换成机器指令,生成的文件叫 可重定向二进制目标文件(目标文件),这个文件在Linux下一般以o为后缀名,在window下一般以obj为后缀名,具体指令如下;
gcc -o 生成文件名 -c .c文件或.i文件或.s文件都可
我们可以使用od指令来阅读我们的可重定向目标文件,如下所示;
这里生成的已经是机器指令了,但是仍然不能直接执行,必须链接后才可以执行;
4、链接
(1)链接做了什么
实际上,我们上面的头文件,如 stdio.h 只有函数的声明,并没有函数的定义,因此我们在是使用我们库函数printf、scanf函数时,光有函数声明时远远不够的,我们必须还有具体的函数定义,这些函数定义都放在动态库或静态库,因此我们链接也有动态链接和静态链接;我们首先完成链接,具体指令如下;
gcc -o 生成可执行程序名 .c文件/.i文件/.s文件/.o文件都可
我们可以通过ldd指令或file指令查看文件使用的动态库/静态库的名字,以及采用何种链接方式;
补充:在Linux下,动态库文件的后缀为.so,静态库文件的后缀为.a,而在window下,动态库文件的后缀为.dll,静态库文件的后缀为.lib;
(2)动态链接
在动态链接中,我们的程序一旦发现有需要使用库函数的代码,则会保存该库函数在动态库中的地址,运行到此部分时,我们直接通过地址跳转到动态库的代码中,执行函数,执行完函数后继续返回程序执行程序后续代码;因此,一旦我们的动态库出现了问题,我们的程序就无法正确执行;
总结:
动态链接优点:可执行程序较小,因为只保存了库函数地址;
动态链接缺点:十分依赖动态库文件,若文件出现问题,程序则可能出现问题;
(3)静态链接
在静态链接中,我们的程序会将需要使用的库函数的定义直接拷贝一份到我们的可执行程序中,我们在运行可执行程序时,无需静态库,我们直接调自己拷贝的代码即可;
总结:
静态链接优点:对静态库文件的无依赖,一旦生成可执行程序,即使静态库被删除,也可以运行;
静态链接缺点:静态链接生成的可执行程序由于会拷贝静态库中的函数定义,因此会变得非常大;
(4)如何使用gcc进行动态链接和静态链接
在gcc中,我们默认使用动态链接的方式进行链接,因此直接使用正常的方式生成可执行程序即可,例如;
gcc -o test-d test.c
而我们若想使用静态链接的方式进行连接,我们加上一个static选项即可,如下所示;
gcc -o test-s test.c -static
补充:有些小伙伴可能没有安装静态库,可通过下面指令安装静态库
sudo yum install -y glibc-static (Centos)
显然,我们使用静态链接生成的可执行程序明显比动态生成的可执行程序要大十倍左右,也符合我们之前的推理,我们在通过ldd和file指令查看我们生成的可执行程序所依赖的库;
test-s是静态链接生成的可执行程序,因此没有依赖的动态库,而test-d是动态链接生成的可执行程序,因此有依赖的动态库,我们可以通过file看出这两个可执行程序是动态链接还是静态链接;
最后
我们的g++与我们gcc的用法几乎相同,我们完全可以用gcc的方式去使用g++,这里我们就不演示了;