目录
1. 背景知识
2.详解翻译的四个过程
2.1 预处理(预编译)
分段编译:预处理(预编译)
2.2 编译
分段编译:编译
2.3 汇编
分段编译:汇编
2.4 链接
翻译阶段:链接
3.内容补充
3.1 gcc的补充
gcc选项
gcc选项记忆
gcc中多文件的操作
3.2 函数库的补充
函数库
函数库一般分为静态库和动态库
❤️我们先通过两张图来简单解释一下整个运行的过程!
❤️从上图我们可以看出整个翻译又分为四个过程:预处理(预编译)、编译、汇编、链接
补充:直接编译
❤️比如:我们写了一个在test.c文件,里面写入代码:
❤️直接编译,默认生成a.out可执行文件;也可以加上-o参数,自己命名生成的可执行程序的名字
❤️无论是a.out 还是mytest直接./a.out或者./mytest都是可以直接就运行的!
❤️在预处理阶段主要包括:a.头文件的展开、b.去注释、c.宏替换、d.条件编译;
⭐️我们主要使用-E参数
⭐️gcc -E test.c -o test.i 第一步走完,停下!临时内容写入.i文件
❤️这里我们也有两种方法进行预编译
⭐️(1)我们再利用-o参数命名tes.i,把结果放到test.i文件里
⭐️(2)利用>重定向到test.i文件中
❤️预处理过后结果还是C语言;我们不妨打开test.i进行查看
❤️在编译阶段主要是把C语言变成汇编语言;
❤️对于编译我们需要用到-S参数;我们利用第一步的结果test.i继续往后编译;它会默认生成test.s文件:
❤️当然我们也可以再用-o选项,进行自己命名:gcc -S test.i -o test.s;也是可以的;
⭐️gcc -S test.i -o test.s 第二步走完,停下!临时内容写入.s文件
❤️在汇编阶段主要是把汇编语言转换成机器指令(.o目标二进制文件,二进制不可执行);
❤️对于汇编我们需要用到-c参数;我们利用第二步的结果test.s继续往后汇编;它会默认生成test.o文件:
❤️当然我们也可以再用-o选项,进行自己命名:gcc -c test.s -o test.o;也是可以的;
⭐️gcc -c test.s -o test.o 第三步走完,停下!临时内容写入.o文件;
⭐️这个二进制文件我们看不懂,所以需要一个工具readelf;readelf -s test.o就可以正常阅读;也可以用od命令,以八进制形式打开;
❤️链接阶段本质上引入我们在代码中常用的第三方库(C库);
❤️对于链接我们不需要参数,直接利用第三步生成的test.o二进制文件;去链接C库,最终生成可执行文件,默认是a.out;也可以用-o自己命名;
❤️gcc会根据文件,进行默认链接(由编译器和文件共同决定);
❤️怎么验证链接的就是C库?我们可以用ldd命令去查看a.out所依赖的库
⭐️静态库和动态库:libc-2.17.so以.so结尾叫动态库;libc.a以.a结尾叫静态库 ;
⭐️库的真正名字:是去掉lib,去掉第一个点(.),.XXX以后的的内容;剩下的就是库名字;
⭐️所以对于libc.so.6这个动态库,真正的名字就是C库(libc.so.6);验证链接的就是C库!
❤️这里我们在补充一下gcc编译器的其它参数和函数库的一些知识点!便于我们的理解!
⭐️-E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面;
⭐️-S 编译到汇编语言不进行汇编和链接;
⭐️-c 编译到目标代码;
⭐️-o 文件输出到 文件;
⭐️-static 此选项对生成的文件采用静态链接;默认生成的是链接,想要生成静态链接,就多加一个参数 -static就可以了;
⭐️-g 生成调试信息。GNU 调试器可利用该信息;
⭐️-shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库;
⭐️-O0、-O1、-O2、-O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高;
⭐️-w 不生成任何警告信息。
⭐️-Wall 生成所有警告信息。
❤️对于我们常用的无非就时编译阶段的参数-E、-S、-c;与对应生成的.i、.s、.o文件!
⭐️对于编译时参数-E、-S、-c;我们就想到vim编译器里,按Esc可以回退到命令模式;这里的Esc不就刚好对应着-E、-S、-c?只需要注意的是,前两大写,最后一个小写;
⭐️对于生成的文件.i、.s、.o;我们就对比下载Centos镜像时,后缀是不是就是.iso?
❤️这一次我们的.c文件只有一个;那如果像在VS中写成项目的模式,最终有多个.c文件,怎么链接成一个可执行文件呢?
⭐️(1)比如有3个.c文件:test.c、test1.c、test2.c我们链接成一个可执行文件;首先要先生成.o目标文件;gcc -c test.c、gcc -c test1.c、gcc -c test2.c会默认生成test.o、test1.o、test2.o文件;
⭐️(2)怎么把test.o、test1.o、test2.o链接起来生成一个可执行文件呢?
直接执行:gcc test.o test1.o test2.o -o a.out;
❤️总结:对于多文件情况,最好是将多文件统一编译成.o文件,然后把所有的.o文件链接生成可执行文件!当然直接把.c文件直接链接也是可以的!
⭐️当.c文件有几千的的时候,我们非要把所有的文件生成.o文件吗?当然不是,这就需要make和makefile,后面会讲解;
❤️我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实“printf”函数的呢?
❤️解释:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,而这也就是链接的作用!
❤️静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为“.a”;
❤️动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”,如前面所述的 libc.so.6 就是动态库;
❤️gcc 在编译时默认使用动态库。完成了链接之后,gcc 就可以生成可执行文件,如下所示:gcc hello.o -o hello
❤️gcc默认生成的二进制程序,是动态链接的,这点可以通过 file 命令验证。