前言
我们前面介绍了yum和vim,可以在Linux上安装和卸载软件了也可以在vim上写C/C++代码了!但代码写完后如何编译呢?这就是我们今天来介绍gcc和g++!
本期内容介绍
gcc和g++
程序的翻译过程
动静态库的链接
一、gcc和g++
1.1什么是gcc和g++
gcc和g++是编译器,用于将源代码编译生成可执行程序。
gcc是C语言的编译器,支持C语言编译。
g++是C++编译器,支持C语言和C++的编译。
同类型的还有clang等。
1.2gcc和g++的使用
我在test.c的文件里写了一个代码:
我现在可以用gcc/g++来编译一下(如果没有gcc或g++的话,直接用yum安装一个)
sudo yum install gcc //安装gcc
sudo yum install gcc-c++ //安装g++
gcc/g++ fileneame
gcc test.c
g++ test.c
此时就会生成一个可执行程序,名字默认为a.out。我们可以./a.out运行
g++编译同理:
注意:gcc只能编译C语言,而g++既可以编译C++也可以编译C语言!
补充:C语言的文件后缀只能是.c ,而C++的可以是 .cpp/.cc/.cxx
如果你不想编译好的可执行程序默认的a.out,可以带-o选项进行指定生成的可执行文件的名字!
gcc/g++ 文件名 -o 目标文件名
gcc/g++ -o 目标文件名 文件名
gcc/g++ filename -o obj_filename
gcc/g++ -o obj_filename filename
1.3新语法编译不过的问题
可能你的gcc或g++的版本较低(可以使用g++gcc -v查看版本),你的代码中使用了一些新的语法特性。从而导致编译不过的问题!此时需要指定更高的标准去编译了!
我们只需要在编译时带: -std=xxx (xxx代表是哪个标准)
例如:-std=c98 / -std=c++11
例如使用c99来编译test.c
二、程序的翻译过程
我们在C语言的时候介绍过,代码变成可执行程序是要经过四步的!分别是:预处理、编译、汇编、链接!这里再来大概回顾并了解一下一些过程的使用场景!!
我们以gcc编译tmp.c为例(g++同理),先创建一个tmp.c并写入内容:
预处理
主要任务: 宏替换、去注释、头文件的展开、条件编译
gcc -E filename -o tmp.i
-E是让gcc从filename开始翻译程序到预处理完成停下(i结尾的是预处理后的文件)
我们可以对tmp.c预处理后让他停下来!
我们可以看看,是不是干了宏替换、去注释、头文件的展开这几步:
明显的看到了tmp.i的行数比tmp.c的多了,近多了800行这就说明头文件展开了,我们发现注释也没了,定义的宏直接替换了!!!而且预处理后还是C代码(是更干净的C语言代码)
OK,没问题,但上面也说了还有个条件编译,这是个啥呢?其实在C语言的时候就见过就是#ifdef这个东西,这里主要是介绍一下它的应用场景(当注释用除外)!例如,vs/idea等是不是有什么社区版和专业版之分!他们的功能是不一样的,专业版的会更丰富!那是不是在写编译器的时候还得写两份源代码?维护的时候是不是也得维护两份呢?显然不是的!一般在一份源代码上添加一些条件编译就可以把功能裁剪开!例如:
1 #include <stdio.h>
2
3 #define v1 1
4
5 int main()
6 {
7 #ifdef v1 //体验版
8 printf("功能1\n");
9
10 #elif v2 //社区版
11 printf("功能1\n");
12 printf("功能2\n");
13 printf("功能3\n");
14
15 #else //专业版
16 printf("功能1\n");
17 printf("功能2\n");
18 printf("功能3\n");
19 printf("功能4\n");
20 printf("功能5\n");
21
22 #endif
23
24 return 0;
25 }
不同的版本在进行对其编译时只需要控制宏定义即可!
当然你也可以不定义宏,在编译时带选项(-D)进行对条件的变量定义进行控制结果:
gcc/g++ -Dv=1 filename -o obj_name
同理不定义就是专业版的可执行程序:
这里有个问题是为什么在编译时还能对代码定义宏呢?原因其实很简单!因为编译器可以裁剪我们的代码!!!上面也看见了,预处理会把注释等去掉,这就充分的说明编译器是可以动我们的代码的,既然你可以动我的代码,那在你编译时让你帮我定义一个宏是不是合情合理呀~!
编译
主要任务:将C语言变成汇编(语法分析那些不在介绍)
gcc -S tmp.i -o tmp.s
-S是让gcc从tmp.i开始对程序进行编译,编译完成就停下来(s是编译后生成汇编的文件)
这里你也可以从tmp.c开始,先预处理在编译。但没必要前面已经有预处理的tmp.i了
我们对tmp.i进行编译,让他生成.s文件:
打开可以看看是否已经是汇编了:
汇编
主要任务:将汇编语言编译成为二进制的目标文件(Linux是.o文件,win是.obj文件)
gcc -c tmp.s -o tmp.o
-o是让gcc从tmp.s开始对程序进行翻译,汇编完成停止
这里也可以从tmp.c开始,和上面同理!
打开tmp可以看看,我们应该看不懂~!
链接
主要任务:形成可执行程序
gcc tmp.o -o my.exe
OK,这就是一个程序的翻译过程!
三、动静态库的链接
刚刚我们介绍了一个程序的翻译过程,预处理、编译、汇编都可以直观的理解,但链接部分链接的是啥?OK,我们来详谈一下链接到底是做什么的~!
3.1什么是链接?
链接是我们的代码和库结合的过程!
什么是库?
库是一组已经被编译好的程序和数据, 可以被程序使用。一般包含:函数、对象等!
库的分类
库分为静态库和动态库!
静态库:在编译阶段被整体拷贝到可执行程序中的库,在运行时不依赖外部文件!
动态库:在运行时才会被加载的库,在编译时程序只保存了库的地址,运行时才会加载动态库文件!
在Linux下,静态库是以.so结尾的, 动态库是以.a结尾的
在windows下,静态库是以.lib结尾的,动态库是以.dll结尾
OK,我们可以看看我们的程序调用了那些库:
ldd you.exe //ldd + 你的可执行程序
这三个就是我们当前程序所依赖的库!中间的libc.so.6是C语言的库!我们可以看看
其中去掉lib和后缀剩下的就是标准库的名字!也就是c标准库!
可是我的系统中也没有什么安装什么库呀?他怎么会有了?
是的你是没有直接安装,但是你在安装你的编译器的时候会给你安装上,不然安装在安装什么?比如你的VS2019在安装时,是会让你选择例如C/C++桌面开发,游戏开发等。他就是让你选择对应所需的库!但是安装完了之后,我怎么知道他库里面有什么方法?这就是头文件的意义~!
3.2为什么要有链接?
1、提高考法效率
2、让开发站在巨人的肩膀上
比如你今天写了一道算法题,其中涉及到了输入输出。如果没有提前写好的这些库函数,你自己就得手搓~!!!我们以前在C语言的时候刷题没有容器什么都得手搓....已经够够的了!你现在连输入输出都没了,一道算法题想了半天会写了,半小时把核心写好了,结果没有输入输出,手搓写了两星期.....这好像效率太低了吧,而且你也不确定你的是否有bug?所以基于此,有一批全球顶级的程序员把常用的方法等实现了经过长期的检验中稳定性等都很好,所以把这些方法等打包成库并配置了相关的头文件!方便开发,提高了开发效率!
3.3如何进行动静态链接的?
动态链接:
在程序执行到库中方法的时候跳转到动态库执行,执行完了回来继续指向当前的程序!
特点:共享动态库,但是一点动态库缺失,所有的动态链接这个库的程序都无法执行!
静态链接:
在程序执行时不再跳到库里去,直接在当前的程序中查找调用的方法即可!
特点:在编译期间把所有库中的方法全部拷贝到当前的可执行程序中,不在关心库!
OK,我用一个栗子来解释一下,动静态链接的过程:
你现在在初三,你是你们学校的第一名,你马上要上市里去读高中了,但是你又比较喜欢上网,这个学校又不让带手机和电脑。但是你家隔壁就是刚刚毕业的学长,你在中考被这所学校录取后,你问这个学长说学校附近有没网吧。你的学长告诉你,学校东门出去向北100米有个小蚂蚁网吧,那里有个9号机,很好!于是在你开学后,周五晚上你想着明天周六,先把衣服一洗,鞋子一刷,明天中午就干了,后天早上起来把物理一写,数学卷子完成就差不多中午了,我去食堂吃个饭再去网吧打一会CF下午三点多了回来再把英语一写就好了!于是周末吃完饭后你就去上网了很潇洒!后来你发现你的舍友同学都在,。有一天因为抢位子你的某个同学没抢到,一气质之下,把这个网吧给举报了,派出所把网吧给查封了!你们都不能去上网了,一学期就这样结束了!你回家之后你爹说儿子这学期咋样,你给你爹把成绩单一给,发现除了化学错了一个选择题丢了两分外其他都是满分,你爹一看高兴坏了,问儿子你有没有什么要的?你给你爹说,我现在需要一台电脑,上次没有全科满分就是因为一个知识点老师没讲所以我才化学没满分,要是我有电脑我早就查了,肯定就满分了!你爹一听没问题就给校长说一下,就给你去网吧把9号机买了下来装在了你的宿舍,你的舍友看了之后,不久也把8号机装在了宿舍,久而久之你们都人手一台了,外面的网吧封了和你已经没关系了,我可以在宿舍自己上网了!
你的学长就是编译器,你的一天的规划就是程序,上网吧就是依赖动态库,网吧没了所有的人上不了网就是动态库一旦缺失所有依赖这个库的程序都无法运行!
你爹把电脑搬到宿舍,就相当于把库拷贝到了程序,即使外面的网吧再出事情也和你上网没关系了,这就是静态库不依赖其他文件!
我们一般的编译器是默认只提供动态库的!gcc默认形成的可执行程序、默认是动态链接!
动态库 && 动态链接的优缺点:
优点:节约资源
缺点:不可丢失
静态库 && 静态链接的优缺点:
优点:一旦形成和库无关
缺点:浪费资源
OK,我们举个例子验证一下:
当前只有这一个可执行程序,我们可以看看,他现在依赖的可和文件类型:
我们可以清楚地看到他是动态链接!!!我们可以把这个tmp.c文件编译成静态库的可执行:
只需在以前的基础上加 -static即可!
gcc tmp.c -o my_static.exe -static -std=c99
显然这个静态的大小更大,直接是动态库的80倍!!!这里也看出了他们的差别!
这也和我们上面介绍的一致,当把库静态链接拷贝到本地后就不在依赖其他库文件了!
注意:一般是没有静态链库的,我这里是安装了静态库所以才只可以执行上面的指令的!如果你没安装请先试用yum安装!
sudo yum install -y glibc-static libstdc++-static
//glibc-static C语言的静态库
//libstdc++-static C++的静态库
OK,好兄弟本期分享到这里就结束了,我们下期再见!
结束语:与其抱怨黑暗,不如提灯前行!