• gcc和g++的使用


    linux编译器-gcc和g++的使用

    image-20221119172130303


    在讲gcc和g++编译同时,我们复习一下程序翻译的大概过程,并以此为例切入使用gcc/g++;

    程序翻译先后进行了预处理、编译、汇编和链接。

    一般我们直接翻译(c语言)程序:gcc test.c -o test.o 【-o +文件名称 指名形成文件的名称】

    (翻译test.c形成可执行程序文件test.o)现在可以细分为以下几个步骤:

    步骤大概过程停止条件目标文件后缀
    预处理头文件展开,去注释,宏替换,条件编译等等—预处理后代码还是c语言-E-i
    编译把c语言变成汇编语言-S-s
    汇编把汇编语言转化为二进制-c(小写)-o
    链接编译器只是编译我们写的代码,还得把我们的代码和库里的代码链接形成可执行程序(你的代码+库)

    记忆:ESc—键盘左上角退出键 对应文件:iso一般指国际标准化组织/感光度/ 镜像文件

    预处理

    gcc -E test.c -o test.i ->预处理test.c文件,-E含义:从现在开始,进行程序的翻译,做完预处理就停下

    -o:指明形成的临时文件名称(.i)【把程序翻译的文件保存在临时文件里,不会显性在Linux中显示出来】

    我原来的代码是这样子的

    image-20221119152021357

    如果不带-o 目标文件直接编译的话会这样

    image-20221119102407187

    编译的内容直接在系统中显示出来;但如果加了-o 目标文件的话,会生成个目标(临时)文件test.i,然后会把这些内容储存在目标文件里

    image-20221119102614510

    我们可以vim查看

    image-20221119102727737

    发现内容是跟显示在系统上是一样的!所以我们在翻译程序的时候,最好指定生成目标文件!

    编译

    gcc -S test.i -o test.s -S含义:从现在开始,进行程序的翻译,做完编译就停下

    -S对应的目标文件后缀为.s

    image-20221119104450075

    生成后我们vim查看test.s 可以看到现在就不是c语言了

    image-20221119104614139

    汇编

    gcc -c test.s -o test.o -c含义:从现在开始,进行程序的翻译,做完汇编就停下

    -c对应的目标文件后缀为.o

    image-20221119105030045

    我们可以查看—发现更看不懂了

    image-20221119105209352

    我们也可以通过od指令查看,发现的确是翻译成了二进制,但这个二进制目标文件不能被执行

    image-20221119105256132

    链接

    gcc test.o 后面不带指定文件则默认生成a.out;带目标文件则生成目标文件

    image-20221119105904178

    image-20221119110001805

    最后我们跑起来

    image-20221119110105153

    🆗,对于程序翻译的复习就到这。

    函数库

    我们实现的c程序里,使用了printf等等函数,头文件stdio.h也只是包含了声明,那具体实现是在哪里呢?系统把这些函数的实现都放在名为:libxxx.so.6的库文件中去,当实现函数时,就去这个库文件里面找,而这就是链接的作用。

    函数库一般分为静态库和动态库。

    静态库指当编译链接时,系统把库文件里的代码加入到可执行文件中,因此生成的可执行文件较大,但程序运行的时候就不需要库文件了。其后缀一般为.a

    而动态库恰恰相反,当运行起来,函数实现时,系统会把链接文件加载到库,这样就节省了系统开销。

    做个形象的比喻:当你需要干饭时,静态库就是在家自己给自己做菜然后吃饭-此时就不需要外面的饭菜提供;动态库就是去外面的饭店吃饭,省出家里做菜的空间。

    动态库和静态库

    动态库Linux系统下库的命名(libxxx.so)-xxx为库的名称windows系统下命名优缺点
    ———后缀为.so后缀为.dll形成的可执行程序小,节省资源-磁盘,内存,网络
    静态库libxxx.a -xxx为库的名称
    ———后缀为.a后缀为.lib形成的可执行程序大;不受库升级或者被删除的影响

    现在我们可以试着去验证,我创建了test.c文件并在里面写好了c语言代码

    image-20221119113943896

    然后我们编译它

    image-20221119115051005

    file 查看可执行程序

    通过file可以看到它是动态链接的-那么我们知道在Linux系统下默认允许程序是链接的动态库!

    ldd 查看可执行程序格式

    可以看到这是动态库,名称为libc.so.6其中c为c库

    所以我们区分库要去掉前缀lib 去掉后缀so,剩下的就是库名称:c

    image-20221119114612712

    我们还可以通过查看知晓动态库

    image-20221119115953143

    我们还可以看看静态库:

    image-20221119115420757

    可以看到链接静态库的可执行程序大小比链接动态库的大了十倍!

    同样的我们可以查看知晓静态库

    image-20221119120140219

    一般而言链接动态库时只能找动态库,不能找静态库,由于Linux系统下大部分指令都需要用到动态库,所以系统会自带动态库,而静态库需要我们自己安装。
    
    • 1

    下面是c语言静态库安装指令

    sudo yum install -y glibc-static

    下面是c++静态库安装指令

    sudo yum install -y libstdc+±static

    好啦gcc和g++的使用就讲到这。

    make和makefile

    make是一个命令,makefile是一个文件。

    1.make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命
    
    令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一
    
    种在工程方面的编译方法。
    2.makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编
    译,极大的提高了软件开发的效率。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    我们先创建一个文件mycode.c,在里面写好程序

    image-20221119154527481

    再创建一个文件,文件名为Makefile(makefile也可);然后在里面写

    image-20221119154657036

    Makefile文件的第一行要写依赖关系【儿子:爸爸 —即儿子依赖爸爸—即前者依赖后者】

    第二行要以table键开头!

    第二行写依赖方法【儿子为什么要依赖爸爸呢?—以这个代码为例编译mycode.c形成mycode—mycode的产生得由mycode.c来决定!】

    然后我们出去make一下,发现执行了依赖方法的代码,然后我们去运行可执行程序发现可以运行

    image-20221119155601211

    然后我们再加点东西进去

    image-20221119155759553

    首先我们来看这个clean这里也是一个依赖关系,但clean冒号后面没有接东西说明clean不依赖任何文件或程序。

    然后它的依赖方法是删除mycode

    我们出去执行,发现它确实能执行

    image-20221119160134125

    但这次clean前面有个.PHONY:clean 这里又与上面mycode的依赖关系和方法有什么区别呢?

    我们连续make,发现后面的都提示mycode已经是最新的了,不需要再编译了

    image-20221119160355686

    但是我们连续make clean,就算mycode已经被删除了,还是能执行make clean,那是为什么呢?

    image-20221119160617497

    别急,我们先来看这个—mycode.c的时间

    image-20221119161133373

    stat 查看文件或目录时间
    Access文件访问的时间—新版的系统对更新频率做了修正,访问过一段时间或者访问了多次才会改变
    Modify内容修改时间—内容改变时间随之改变
    Change文件属性修改时间—文件属性改变时间随之改变(内容修改了文件属性也会变-比如文件大小等等)

    然后我们改变一下mycode.c的内容

    image-20221119162439503

    我们发现时间变了。

    是否能make 是根据依赖关系两边的文件时间改变而决定的。

    我们知道编译文件产生目标文件,所以编译文件修改时间在目标文件修改时间之前。比如编译mycode.c产生mycode,那么mycode.c的修改时间肯定在mycode的修改时间之前,如果是这样那么Makefile文件识别到这样就不需要在make了

    如果make之后我们去修改了mycode.c,那么mycode.c文件的修改时间就在mycode修改时间之后。—编译文件修改时间在目标文件修改时间之后,这合理吗?所以Makefile要make一下。

    我们可以实验去证明。另外我们知道。touch不仅可以创建文件,还可以刷新文件时间。

    我们先make 文件mycode.c直到不能再make,然后我们touch刷新时间,之后我们发现又能make了!

    image-20221119163427859

    峰回路转,我们得出结论,.PHONY的作用是不再用时间作为指标来执行make!

    .PHONY:,冒号后面的是伪目标(或伪文件)—不再用时间作为指标来执行make!

    所以我们可以试试在前面加.PHONY

    image-20221119164247859

    我们出去make发现确实如此!

    image-20221119164229561

    另:makefile默认情况下 从上到下自动执行第一个目标文件,这就是为什么后面的clean要我们另外执行的原因。

    实际上形成目标文件mycode是要通过前面说到的对mycode.c进行预处理形成mycode.i,编译形成mycode.s,汇编形成mycode.o,最后链接才产生可执行文件mycode。所以依赖关系并不是按照从上到下,而是按照推导规则来的!

    我们可以打乱顺序

    image-20221119165845208

    然后去make

    image-20221119165931226

    我们发现make出来的顺序并不是按照在Makefile文件上从上到下那样的顺序,而是按照编译的逻辑顺序来的!

    好啦对于make和makefile的介绍就到这里了。这里的各自推导规则有点难以理解,学习这里的小伙伴要有耐心噢。

    最后我们来总总结一下这篇文章,

    我们用翻译程序的过程来引出对g++/gcc的使用,翻译程序的过程有:预处理—编译—汇编—链接;前三者的停止条件为:ESc,对于产生的文件后缀为:iso;链接有链接动态库和静态库,linux下动态库为libxxx.so(后缀为.so,xxx为库的名称),静态库为libxxx.a(.a为后缀)

    Windows下动态库后缀为.dll,静态库后缀为.lib ;

    然后到make和makefile,makefile第一行是依赖关系,第二行是依赖方法,要以table键为起始;默认自动从上到下make第一个目标文件;依赖关系按照产生目标文件的程序逻辑走;.PHONY:伪目标 这个目标不以时间为标准来make;

    看到这里的观众老爷们不妨点点关注点赞收藏走一波,你们的支持是我更新的不懈动力~~~

  • 相关阅读:
    市场饱和,你得抓住Android初级开发破局关键点---Framework
    英语学术论文简短语句摘抄
    【面试题精讲】Java字符型常量和字符串常量的区别?
    列举一些常用的Webpack配置和插件
    Shell 流程控制
    redis学习笔记(二)--redis实现原理相关
    浅讲make/makefile【linux】
    C语言文件操作
    鸿蒙应用程序包安装和卸载流程
    裴蜀定理(详解)
  • 原文地址:https://blog.csdn.net/m0_71841506/article/details/127939343