Linux gcc、g++编译

目录

强烈建议全文阅读!

1、设计样例

2、程序的翻译过程

3、链接-动静态链接-各自特点和区别

一、是什么?

二、为什么?

三、怎么办?(怎么做到链接的?)


gcc:专门编译C语言
g++:编译C语言和C++


1、设计样例


gcc如果不是9.3.1版本,可能不支持for循环
编译时要加上-std=c99才可以#使用C99的标准
gcc xxx1 -o xxx2 :将xxx1文件编译为 -o紧跟的文件名xxx2文件

写C++代码文件后缀不能写错
要么是xxx.cpp
或者是.cc文件
xxx.cxx也可以

gcc不能编译CPP代码
但是g++和gcc编译语法是一样的
为了支持更高级的语法选项,编译时可以加上-std=c++11#使用C++11的标准

mv xxx1 xxx2#重命名xxx1


2、程序的翻译过程


翻译过程:预处理、编译、汇编、链接
预处理:去注释、头文件展开、条件编译、宏替换
编译:检查语法,把C语言变成汇编语言(报错:语法报错、链接报错、运行报错)
汇编:将汇编语言编译成为二进制目标文件(这种二进制目标文件没办法直接执行)
链接:链接库文件,将之形成可执行文件

分步编译:
第一阶段:预处理
gcc -E xxx -o test.i#形成临时文件
从现在开始进行程序的翻译,预处理完就停下
但是翻译出来的.i文件有几百行,多出来的行是哪里来的?
是从头文件展开出来的,同时还拷贝了该文件自带的头文件 
也就是说,经过预处理之后,头文件也就没用了
而一般来说,我们在配置g++时间,会把相关的头文件和库文件都放在了/user/include文件目录下
因此,我们在windows系统下下载的vs2022编译器,实际上也会伴随着下载配套的头文件

打开.i文件和源文件对比:去掉注释、宏替换、头文件展开

在不同的编译器版本,有些代码内容可能不一样
那么就意味着要维护多个代码版本
这会增加维护成本
所以,为了解决这个问题
就增加了一个叫做条件编译的东西
通过条件编译,就可以只维护一份源代码
对应不同的场景可以在编译阶段进行动态的裁剪
那部分不需要编译,那部分需要编译
通过动态的条件编译之后
一份源代码就可以适应多个不同的场景使用
增加了源代码的容错以及维护的成本
这就是条件编译

条件编译格式:
#define XXX

#ifdef X1
...
#elif XX2
...
#else
...
#endif
...

通过定义宏的方式对代牧进行动态的编译裁剪
同时了,可以通过命令行的方式对文件进行编译处理:
gcc -DXX=1  file#命令行添加宏

第二阶段:编译:变成汇编语言
(也可以从.c文件开始,只是多了一步预处理)
gcc -s test.i -o test.s
-s的意思:从现在开始进行程序的编译,编译完成就停下来

第三阶段:汇编:将汇编语言翻译成为二进制目标文件
gcc -c test.s -o test.o(.o的意思是.obj的意思,object file 可重定向目标文件)
-c:从现在开始进行程序的汇编翻译,汇编完成就停下来

第四阶段:
链接:
gcc test.o -o my.exe(一步到位)

预先处理、编译、汇编:-ESc -iso


程序的翻译为什么是这个过程?
在最刚开始的时候,编程都是用打控制带进行的
有孔,透光,就为1;没有孔,不透光,就为0
通过一条纸带,给机器输送二进制指令
但是,后来的人们觉得很麻烦,所以就做了汇编语言(例如各种注记符,本质上就是二进制指令)
再往后的发展,就有了变成语言,例如C语言
同时,只要不是二进制代码,机器就看不懂
所以,就需要将对应的语言翻译成二进制代码
例如,有汇编的编译器,有语言的编译器
但是,编译器的书写是很难的
我们是可以从编程语言代码直接编译到二进制代码
但是,很麻烦,尽管技术上可以解决
到那时,如果这样做,那么前面的阶段
即汇编语言到二进制代码的编译成果就完全没有用上
几乎等于从零开始开发
有必要吗?完全没有必要。
所以,为了利用上前人的成果
我们选择了从编程语言编译成汇编语言,再从汇编语言编译成二进制代码
所以,总的来说
今天的我们都站在巨人的肩膀在看世界
这也是一种历史的惯性发展
这就是为什么编译是这样的过程


但是:第一个汇编语言怎么编译成二进制代码呢?
第一个汇编语言写出来的时候,又没有对应的编译器。就算我发明了汇编语言,也无法编译成为二进制代码
那么将毫无意义
所以,到底是先后汇编语言还是先有编译器呢?
肯定是先有人用二进制写了一部分汇编语言
先有语言,才会有将汇编语言翻译成为二进制代码的冲动

同时,用二进制写的汇编语言的编译器本质也是一个软件,编译的软件
这个时候,我就可以再用汇编语言的编译器软件写一个纯汇编语言的编译器
也就是说,我现在这个汇编的编译器是用汇编语言写的,而不是二进制写的
那么,二进制写的汇编语言的编译器就不需要了

同时,当C语言出来以后,首先要用汇编语言写一个C语言的编译器
然后,再用C语言的编译器写一个纯C语言的C语言编译器
就这样,汇编语言写的C语言编译器也没用了
上述这个过程,叫做编译器自举的过程

刚听这首歌的时候,我还是一个小男孩,现在,我已经是两个孩子的妈了 


3、链接-动静态链接-各自特点和区别


一、是什么?


是一个把我们的程序和库结合在一起的过程
语言一定要有自己的标准库
ldd XXX.exe#查找文件到底依赖的,是哪一个动态库。(这个链接的动态库,是二进制的文件,而且是在系统装好了的)
它会将可执行文件所依赖的动态库给展示出来
如果是C语言,链接的就是C标准库
那么,库里面是什么?有什么?
例如我们使用的printf、malloc等等方法函数的实现,都是打包在这个C标准库内的
我们写了一个printf、本质上就是一个接口调用而已
也即是说,有些方法我们不需要自己书写了,而是直接进行调库即可
但是,有那么多的方法,成千上万,我怎么直到要用哪一个呢?
所以,就有了各种头文件XXX.h
这个头文件就相当于一个实现方法的说明书
例如,math.h里面,这个头文件告诉我们:
它包含着各种相关的数学的方法,以及各个方法对应的参数、返回值等,方便我们调用

所以,事实上,我们在安装C语言开发环境的时候
还需要安装的有对应的编译器、C标准库和C头文件
我们在安装Vs时,那个选择开发环境的界面,事实上选择的就是C标准库和C头文件

所以,链接就是:
把我们的可执行程序编译链接,然后和标准库进行链接,最后形成一个可执行的目标文件
这个库又是什么呢?
是大佬给我们写好的各种方法的集合,用库和头文件的方式让我们调用


Linxu:(库的真正的名字:libc.so.6)
动态库:.so
静态库:.a

windows:
动态库:.dll
静态库:.lib


二、为什么?


1、提高开发效率和安全性(库是大佬写的,所以效率性、安全性更高)
2、站在巨人的肩膀(库是大佬写的,所以你不用再写,你也不会写)

三、怎么办?(怎么做到链接的?)


Linxu:(库的真正的名字:libc.so.6)
动态库:.so
静态库:.a
因为存在两种链接库,所以存在两种链接方式:即动态/静态链接
动态链接:(默认的链接方式)
在编译链接的时候,编译器就会将目标库的地址,也就是动态库的地址给好
我们执行某一个文件的编译链接
当运行到某一行需要动态库的时候
就会按着这个地址去既定的位置找到这个动态库
拿到对应的方法,并且执行,传参的传参、返回的返回
拿到结果后,继续执行后面的代码
这样的链接模式,会导致的是,一旦动态库缺失,那么所有和这个库进行动态链接的程序都无法执行
甚至编译错误

而静态链接是什么呢?
和动态相对的
就是不再去相应的动态库去调取方法了
而是在编译的时候,直接把库中的方法直接拷贝,干到自己的可执行程序中
直接在自己这里实现了
所以,就不再需要去进行动态库的链接
所以,谓之静态


动态库 && 优缺点:
1、不能丢失
2、只需要备份一份,即可共享所有的文件(节省资源)

静态库 && 优缺点:
1、一旦拷贝形成,和动态库无关
2、浪费资源

形成静态可执行文件的编译:
gcc -o mytest-static test.c -static

静态库在哪?
默认情况下,Linux下,一般静态库都是默认没有安装的
怎么安装?百度搜索,全都有
Linux最全环境配置 + 动态库/静态库配置 - 知乎 (zhihu.com)

静态链接应用场景?
要求程序具有强大的跨平台性时,就用到了


 

  • 19
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二十5画生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值