目录
一、前言
二、编译器翻译过程
1.预处理
1.宏替换、去注释
2.头文件展开
3.条件编译
2.编译
3.汇编
4.链接
三、条件编译&&初步理解链接
1.条件编译
1->是什么?
2->使用场景
2.初步理解链接
编辑
1.库是什么
2.动态库&&动态链接&&优缺点
3.静态库&&静态链接&&优缺点
3.关于动静态链接的补充
1.后缀
2.如何改变链接方式
四、gcc其他选项 - 了解即可
五、小结
一、前言
前面博主向大家介绍了Linux基础开发工具yum和apt,还有我们开发中最重要的vim编辑器。yum和apt是核心功能是软件包安装,而vim编辑器的核心功能是编写代码的!有了前面的基础,我们还需要,学习编译器——gcc和g++。gcc和g++仅用来做编译工作。它不像我们平时开发用的visual studio。
GCC 和 G++ 是命令行编译工具,主要负责预处理、编译、汇编和链接,不提供编辑和调试环境;Visual Studio 是 IDE,集成了代码编写、编译、调试等功能。
二、编译器翻译过程
gcc编译选项格式gcc [选项]要编译的⽂件[选项] [⽬标⽂件]
1.预处理
预处理阶段核心功能是把c语言转换为干净的c语言。
命令:
gcc -E code.c -o code.i-E选项的意思就是告诉编译器做完预处理就停下。
预处理阶段具体动作表示为:宏替换、去注释、头文件展开、条件编译等等。
1.宏替换、去注释
把我们定义的宏和对代码的注释全部去掉。
2.头文件展开
操作内容:把我们include的头文件里的内容拷贝一份到原文件。
具体操作中可以看到:原文件的大小会变大。
3.条件编译(下文详讲)
2.编译
编译就是把已经预处理好的源文件转换为汇编语言
命令:
gcc -S code.i -o code.s-S选项的意思就是做完汇编就停下。
3.汇编
汇编就是把汇编语言转换为二进制点o文件。
命令:
gcc code.s -o code.o-o选项的意思就是做完编译就停下。
4.链接
链接就是把编译好的二进制.o文件和库统一链接到一起,形成二进制可执行文件。
命令:
gcc code.c -o code一般程序的构建过程就是先把源文件编译成对应的.o文件,然后再统一链接形成可执行程序。这样做可以提高编译的效率。我们可以想象,在一个大型项目中,可能有成百上千个
.c文件。如果每次修改一行代码都要把整个项目重新编译一遍,那将极其耗时。
所以编译被拆成了几个步骤:
编译(
-c):把每个.c文件单独翻译成.o目标文件。修改了哪个文件,就只重新编译哪个。链接(Link):把所有的
.o文件,加上系统库(比如libc),像搭积木一样拼装在一起,最终生成一个可执行文件。
三、条件编译&&初步理解链接
1.条件编译
1->是什么?
现在大家见一下条件,编译是什么:
#include<stdio.h> void v1() { printf("this is 免费版\n"); } void v2() { printf("this is 升级版\n"); } int main() { #ifdef V1 v1(); #else v1(); v2(); #endif return 0; }
#ifdef V1:这是一个预处理指令。意思是"If Defined"—— 如果宏V1被定义了。如果定义了
V1:编译器就只编译v1();这一行。
#else:如果没有定义V1。如果没定义
V1:编译器就会编译v1();和v2();这两行。
#endif:结束这个条件判断。
条件编译是可以对我们的代码进行裁剪的,在预处理期间。预处理符是给编译器看的。
2->使用场景
我们可以类比,我们平时下载的软件有免费版和收费版。v1提供的功能就是免费版,v2就是在v1的基础上有所提升的功能就是收费版。所以条件编译可以用来进行多商业化软件,进行不同种类的管理。
2.初步理解链接
1.库是什么
以printf为例:
用最通俗的话来说:库(Library)就是别人已经写好的、经过测试的、可以直接拿来用的代码集合。
打个比方:
不用库(自己造轮子):你想打印一句话到屏幕上,你需要自己写代码去调用操作系统的底层显卡驱动,控制像素点在屏幕上画出字母。
用库(用现成工具):前人已经把这些复杂的底层操作封装好了,变成了一个叫
printf的工具。你只要把它“拿过来”用就行。这个装满了各种现成工具的“百宝箱”,就是库。
2.动态库&&动态链接&&优缺点
动态库通常和程序进行动态链接。
情景一:比如你是小王,你进了一所高中,你想考大学,而有一个叫张三的学长,顺利的考入了清华大学。你想着上学期间总要有一个娱乐活动吧,你就问这个学长,学校附近哪里有网吧?张三呢就告诉你一个地址。你在学校有一天的计划表,上午上完课,中午计划去网吧放松一下。你按照张三学长给你的地址,顺利的找到了这家网吧。网吧老板告诉你一个电脑号你又在那儿坐了下来开心的打了两个小时的无畏契约。
上述情景我们可以把小王想象成是一个程序,张三是编译器,小王所在的高中是内存,小王列的一天的计划表就是一个个的进程,网吧就相当于是一个库,电脑号就是要找的库函数。
张三给小王地址这个动作可以想象成是链接器向程序写入链接地址,就是向程序中写入库函数的地址。小王在学校列一天的清单,就相当于是程序在执行时要用到的,一个个的进程,中午的时候要去上网,就是需要用到这个动态库地址,运行到这一条进程的时候,程序就会根据链接器给的地址找到库函数,从而查找和运行库程序。
此时这个网吧就相当于是一个动态库。张三给小王网吧地址这个动作就相当于是动态链接。显而易见的是,动态库和动态链接最大的缺点就是一旦库文件丢失,所有的程序直接崩溃。而动态库和动态链接最大的优点就是节省资源,动态库就相当于我们现在的共享经济。
3.静态库&&静态链接&&优缺点
静态库通常和程序进行静态连接。
情景二:小王去网吧这个行为被学校给发现了,学校就找帽子叔叔就把这个网吧给拿下了。小王这时候就没有办法去网吧了,但是小王还是想上网,如果不放松一下的话,学习不进去,所以他就找他爸,让他爸给他弄了一台电脑中午玩。他爸就跑到那个网吧,找网吧老板要了一台小王之前用的电脑。
上述情景中的小王他爸就相当于是一个静态库,小王他爸给小王一台电脑这个动作就相当于静态链接。映射到链接过程就是把程序要用到的函数实现给该程序拷贝一份。
可以看出静态链接和静态库最大的优点就是程序不依赖其他的库。也就是小王上不上网这个行为和这个网吧老板已经无关了。而缺点就是小王桌子上放一台电脑会很占位置。映射到链接过程就是将这个可执行程序变大了,运行内存的时候,加载的内存占据更多的内存空间。
3.关于动静态链接的补充
1.后缀
| 操作系统 | 静态库后缀 | 动态库后缀 | 命名习惯(示例) |
|---|---|---|---|
| Linux | .a(Archive) | .so(Shared Object) | libmylib.a/libmylib.so |
| Windows | .lib(Library) | .dll(Dynamic Link Library) | mylib.lib/mylib.dll |
2.如何改变链接方式
linux下链接默认是动态链接。
如何强制进行静态链接?
在命令行末尾加一个-static选项!
四、gcc其他选项
了解即可
-E 只激活预处理,这个不⽣成⽂件,你需要把它重定向到⼀个输出⽂件⾥⾯-S 编译到汇编语⾔不进⾏汇编和链接-c 编译到⽬标代码-o ⽂件输出到 ⽂件-static 此选项对⽣成的⽂件采⽤静态链接-g ⽣成调试信息。GNU 调试器可利⽤该信息。-shared 此选项将尽量使⽤动态库,所以⽣成⽂件⽐较⼩,但是需要系统由动态库.-O0 、-O1、-O2 、-O3 编译器的优化选项的4个级别,-O0表⽰没有优化,-O1为缺省值,-O3优化级别最⾼-w 不⽣成任何警告信息。-Wall ⽣成所有警告信息。
五、小结
这篇博客说白了就是把Linux下gcc/g++这套编译流程捋了一遍:从预处理、编译、汇编到链接,每一步gcc在干什么、对应什么命令,都过了一下。预处理阶段会做宏替换、去注释、头文件展开和条件编译;后面再一步步生成汇编、目标文件,最后链接成可执行程序。中间还拿“免费版/升级版”举例讲了条件编译,顺便把动态库、静态库以及动态链接、静态链接的优缺点也初步聊了聊:动态链接省资源但怕库丢失,静态链接不依赖库但程序会变大。最后也补充了Linux和Windows下动静态库的后缀区别,以及怎么调整链接方式。
不过问题也来了:现在编译一个文件还好,手敲gcc命令还能忍;要是项目里文件一多,每次改完都手动敲一堆编译命令,那就有点折磨人了。所以下一篇就顺理成章进入自动化构建——也就是make/makefile。简单说,Makefile就是把这些编译规则、依赖关系提前写清楚,make再根据文件有没有改动,自动决定哪些要重新编译,省得我们一遍遍手敲命令。这样从“会编译”到“会管理编译”,开发效率就能往上提一截了。