Linux工具学习之【gcc/g++】

简介: 书接上文,我们已经学习了 Linux 中的编辑器 vim 的相关使用方法,现在已经能直接在 Linux 中编写C/C++代码,有了代码之后就要尝试去编译并运行它,此时就可以学习一下 Linux 中的编译器 gcc/g++ 了,我们一般使用 gcc 编译C语言,g++ 编译C++(当然 g++ 也可编译C语言),这两个编译器我们可以当作一个来学习,因为它们的命令选项都是通用的,只是编译对象不同。除了编译器相关介绍外,本文还会库、自动化构建工具、提权等知识,一起来看看吧

✨个人主页: Yohifo

🎉所属专栏: Linux学习之旅

🎊每篇一句: 图片来源

🎃操作环境: CentOS 7.6 阿里云远程服务器


Whatever is worth doing is worth doing well.


任何值得去做的事情,都值得把它做好。

be7f4ab28177ac85a4a17975a48b3fc.png

📘前言


书接上文,我们已经学习了 Linux 中的编辑器 vim 的相关使用方法,现在已经能直接在 Linux 中编写C/C++代码,有了代码之后就要尝试去编译并运行它,此时就可以学习一下 Linux 中的编译器 gcc/g++ 了,我们一般使用 gcc 编译C语言,g++ 编译C++(当然 g++ 也可编译C语言),这两个编译器我们可以当作一个来学习,因为它们的命令选项都是通用的,只是编译对象不同。除了编译器相关介绍外,本文还会库、自动化构建工具、提权等知识,一起来看看吧

6c278209ef6baab8617352e4bac7e73.png


📘正文


📖gcc/g++ 命令


在接下来的学习中,我们以 gcc 为例,因为两者选项都是通用的,所以也就相当于间接学习了 g++ ,这个编译器上手还是很简单的,选项也不是很多


注意: 如果命令失效,很有可能是没有下载 gcc/g++ ,需要自行下载安装 gcc 与 g++


📃-o 目标文件


gcc 源文件 默认会将代码编译链接并生成可执行文件 a.out ,当然前提是代码没问题,所以这样看来编译一个文件还是很简单的


$ gcc 源文件 //直接编译源文件,生成默认可执行文件为 a.out


27309ef24def863978b2c1edceac273.png

可能有的人不想让它生成默认的 a.out ,想生成为指定文件,没有问题,直接通过 -o 选项就能实现

注意:-o 选项后面必须紧跟生成的目标文件,这个选项可以放在源文件后面,也可以放在前面


$ gcc test.c -o OK  //编译生成文件为 OK
$ gcc -o OK test.c  //这种写法也是可以的


在我们使用 gcc/g++ 时,都可以通过 -o 选项生成指定文件


📃-E 预处理


在C语言学习阶段,我们学习了源文件变成可执行文件的过程,即预处理-编译-汇编-链接,当时因为没有学习Linux,没法很好的展示各个环节的现象,今天可以来详细看看


首先是第一步:预处理,又称预编译


会进行头文件展开、删除注释、替换宏、执行条件编译等操作

目的是生成一个纯粹的C代码程序

经过预处理后的文件后缀为 .i

我们可以直接通过 gcc 中的 -E 命令,使编译器在执行完预处理后停下来,配合 -o 生成指定文件,这样我们就可以观察到上面所提到的这些现象了

$ gcc -E test.c -o test.i //预处理后的文件后缀为 .i 此时仍然是C语言



6252f766ef2dbe2dfb2d5f304ea789c.png

预处理就像是过滤,会把代码进行检查删除,留下纯粹的C代码,方便后续进行转换


📃-S 编译


下面进入第二个步骤:编译


进行语法分析、词法分析、语义分析、符号汇总等,然后将合法的代码转为汇编代码

编译目的是生成汇编代码

编译后生成的文件后缀为 .s

编译阶段比较重要的一步就是符号汇总,它会各种符号汇总起来,方便后续符号表的形成,符号表用于各种函数间的相互调用


我们可以通过 -S 选项,使 gcc 在执行完编译阶段后就停下来,配合 -o 生成文件 test.s


$ gcc -S test.c -o test.s //可以直接从 test.c 开始执行,也可以从上一步中的 test.i 执行

e064c1695665b7f35ca002adf2957ed.png



📃-c 汇编


接下来进入第三步:汇编


主要任务是将汇编代码转为二进制,并生成符号表

二进制文件的格式是 elf ,此时 vim 查看为乱码

生成的文件后缀为 .o

因为计算机只能看懂二进制,所以将代码转为二进制是必须进行的操作,除此之外,还有一个重要步骤:生成符号表


关于符号表


这个东西相当于函数独一无二的地址,在Linux 中,C语言的符号表比较简单,通常是 _函数名,比如 _Add ;C++更详细一些,通常为 _Z函数名长度+函数名+参数1+参数2 ,比如常见的 Add 函数,生成的符号表为 _Z3Addii ,这里的参数是两个整型,这也是C++支持重载,而C语言不支持重载的根本原因,毕竟C语言中两个重名的函数生成的符号表是完全一样的,区分不了

可以通过 -c 选项使 gcc 在执行完汇编阶段后就停下来,指定保存文件为 test.o


查看生成的 test.o 文件,可以用 readelf 这个工具,缺失的可以去下载


$ gcc -c test.c -o test.o //从源文件重新开始编译,生成 test.o 二进制文件
$ gcc -c test.s -o test.o //从上一步中生成的 test.s 文件开始编译,两者效果是一样的
//关于查看 elf 格式的文件
$ readelf -a test.o //可以通过软件,观察到符号表等信息

cfb7123a3a8dd52c2bb85d06d1382cd.png



📃gcc 链接


下面是最后一步:链接


进行合并段表、将符号表进行合并和重定位等

将程序运行所需的各种函数链接起来,包括与库函数的链接,Linux 中一般是动态链接,链接后生成可执行文件,此时的文件也是 elf 的格式

gcc 默认生成的可执行文件为 a.out,我们可以指定生成任意文件

$ gcc test.c -o myfile  //生成可执行文件为 myfile
$ gcc test.o -o myfile  //继上一次生成的二进制文件执行链接,也是没有问题的

22d09c3dac963bd28a38b513e8b3fba.png

以上就是本文关于 gcc/g++ 的全部内容了


📃小结


关于各个命令选项可以巧记为 ESc 这是键盘上的一个键,忘记了可以看看

还有各个选项对应生成的文件后缀为 iso


下面还会介绍程序相关链接情况


📖库


众所周知,每种编程语言都有属于自己的库,比如我们C语言中的 stdio 、string、stdlib 等等标准库,当我们程序在调用库函数时,就是在调用标准库中的函数,而这些标准库都在 /usr/include 这个目录中,这个文件就是 Linux 中的C语言动态库;除了 动态库 外还有 静态库


be808bd2090ea31305b6f9dbe6e5971.png


📃动态库


动态库 即通过 动态链接 的库,动态库 又称 共享库,因为 动态库 中的内容是被所有程序共享的,简言之 动态库 中的代码只需要存在一份,程序需要使用时,直接通过对应位置调用就行了


Linux 中默认使用 动态链接 的方式,我们可以通过指令 ldd 最终生成的文件 来查看最终生成文件的链接情况


$ ldd 最终生成的文件 //查看文件的链接情况



libXXX.so 是动态链接的标志


其中 lib 是前缀

.so 是后缀

去掉前缀与后缀,就是最终调用的库

举例:libc.so 去掉前缀与后缀,最终为 c ,可以看出文件最终调用的是C语言共享库,即 动态链接


动态链接 主要依赖不同函数在库中的位置信息进行调用,只有一份代码库,比较节省空间


我们还可以通过 file 命令查看文件详细信息

$ file 最终生成的文件  //查看文件的详细情况

501b862c627cb08267a9c1b14129ff7.png


这也验证了 Linux 默认使用 动态链接 的现象


类比记忆


动态库 就像是网吧(假设只有一家),那么全校的同学都可以去网吧中上网,还可以根据自己的喜好选择自己喜欢的机位,当然前提是你知道在哪个位置


3994e7f2348d9c0d2c7398cbf423173.png

📃静态库


除了 动态库 外,还有 静态库 ,采用 静态链接 的方式;静态链接 不同与 动态链接 共享的方式,如果程序调用 静态库 ,会将自己所需要的代码 拷贝至程序中 ,完成拷贝后,后续不需要再调用 静态库


如果想采用 静态链接 链接的方式编译程序,需要在编译时加上 -static 选项,当然前提是得有 静态库,没有的可以通过 yum install -y glibc-static 下载 静态库


当然我们也可以通过 ldd 最终生成的文件 查看是否为 静态链接


$ yum install -y glibc-static //下载静态库
$ gcc test.c -o myfile-static -static //采取静态链接的方式编译程序
$ ldd 最终生成的文件 //查看文件的链接方式

77086769af8a6571c17240a8e0936ec.png

静态库 命名为 libXXX.a


lib 是前缀

.a 是后缀

去掉前缀与后缀,就是最终调用的库

我们也可以采用 file 命令查看详细信息


$ file 文件 //查看详细信息

ecb4a2ae0045278cda8b2062fc7c4fc.png

静态链接 因为是直接将需要的代码拷贝到程序中,因此最终生成的文件会变大,比较占空间

58b5ee97c01aeb19870454e890ccaf5.png


因为这种方式很占空间,所以 Linux 中默认使用 动态链接 的方式


类比记忆


静态库 就像是把网吧里的电脑,买了一台同款的在自己寝室(调用某个函数),一台还好,如果买了很多台,寝室自然就没有空间了

📃优劣比对


动态库 和 静态库 各有优缺点,不然也不会同时存在两种库了


区别 动态库 静态库
调用方式 通过函数位置进行调用 直接将需要的函数拷贝至程序中
依赖性(运行时) 需要依赖于动态库 可以独立于静态库运行
空间占用 共享动态库中的代码,空间占用少 拷贝代码会占用大量空间
加载速度 调用函数,加载速度慢 直接运行,加载速度快

小结

动态库


优点

可以实现不同进程间的资源共享

对于函数的升级只需要替换动态库文件,不需要重新编译程序

可以控制是否加载动态库,不调用函数时就不加载

缺点

需要调用函数,加载速度较慢

程序运行需要依赖动态库

静态库


优点

所需函数直接拷贝至程序中,运行速度快

程序运行无需依赖库,便于移植

缺点

对于函数的升级,需要重新进行编译

同一份代码可能出现重复拷贝的情况,浪费空间

📖自动化构建工具


自动化构建工具可以帮助我们完成设置好的指令,指令为 make ,我们可以通过提前设置,实现源文件的快速编译


📃Makefile 文件


要想使用 make 指令,就得先有 Makefile 文件,Makefile 文件中主要编写任务,而任务由 依赖关系 + 依赖方法 构成


1.依赖关系


比如源文件为 test.c ,编译后生成的文件为 myfile ,那么两者间的 依赖关系 为 myfile:test.c 这组 依赖关系 我们可以写入 Makefile 文件中

2.依赖方法


有了关系后,就要描述具体实现方法,比如上面那组 依赖关系 的 依赖方法 为 gcc test.c -o myfile 将 依赖方法 也写入 Makefile 文件中

5a519a27bd12102a9e459a0420e5e17.png


完成上面两个内容的编写后,我们就得到了一个基本的自动化任务,输入 make myfile 即可编译 test.c 文件,生成 myfile


$ make myfile //执行自动化指令,编译 test.c 文件

1


0dd089d2f36086897a5329404ccc7c3.png

注意: 同一个自动化任务,执行成功后,如果相关文件最近没有发生改变,那么无法再次执行自动化任务


📃make 指令


上面展示了如何编写 Makefile 文件并执行相关任务,使用了 make file 指令,并没有直接使用 make指令,因为这个指令还是有些说法的


单纯输入 make 指令时,默认执行 Makefile 中的第一个任务,当任务成功执行后,不再继续执行后续任务(一个 Makefile 文件中,可以有多个任务),由此可见,单纯的 make 指令只会执行第一个自动化任务



当我们编写好 Makefile 文件后,可以通过 make 任务名 调用任务,任务名就是 依赖关系 中的左侧名;也可以直接通过 make 调用第一个任务

693ca94117fbfbec77d17415183ab43.png

📃任务刷新策略

前面说过,同一个方法如果成功执行过,在原文件最近修改时间没有发生变化时,无法再执行任务,这背后的原因是方法是否执行会先判断生成的目标文件是否为最新,如果为最新,就不再执任务


举例:重复执行 make myfile 任务


$ make myfile //第一次执行任务,成功
$ make myfile //第二次执行任务,失败,因为源文件最近没有被修改

708493875cebbd691529e50634d619d.png


想要再次执行任务也很简单,对源文件做出修改,或者直接 touch 一下源文件就行了,两种行为都会修改文件的最近修改时间,使源目标文件不是最新时间


📃.PHONY 伪目标


.PHONY 是 Makefile 文件中的一个关键字,意为对某某对象生成伪目标,这样就能在不对源文件进行修改的情况下,重复执行任务了


//Makefile 文件中
.PHONY:myfile


e420bb3e9f5c21309a10f0a90e14dc6.png

在使用关键字 .PHONY 对目标进行修饰后,可以无视任务刷新策略,重复执行任务了


不过这有什么意义呢?

答:对于这种源文件来说,没有任何意义


.PHONY 这个关键字,一般是用来修饰 clean 任务,即清理解决方案,Makefile 实现为


//Makefile 文件内
.PHONY:clean
clean:
  rm -r myfile

460bf42460956bb2635aaf91c8e2d3c.png


换个角度想想,当我们把生成的原目标文件清理后,再执行任务,生成目标文件是一件很合理的事,也完全符合任务刷新策略


由此来看,.PHONY 也是很有用的


注意: 像 clean: 这种半缺失 依赖方法 是合理的,毕竟清理这个任务也不需要任何对象,只需要单纯的执行删除(清理)指令就行了


📃补充


make 指令的工作原理是去 Makefile 文件中寻找任务执行,它的设计者为了确保普适性,创建 makefile 文件也是合法可用的


也就是说,我们创建 make 指令的任务源文件时,可以创建为 Makefile ,也可以创建为 makeile


📖sudo 提权


权限,是一个让人又爱又恨的东西,它的安全性固然很重要,但有时候又太麻烦了,当我们普通用户想执行操作时,需要请 root 出马,比如最基本的下载软件指令,感觉有些小题大做了


为了解决这种不合理的现象,Linux 中就有 sudo 提权 这个概念,简单来说,就是暂时借助 root 的身份去完成某条指令


$ sudo yum install -y sl  //暂时提权下载软件

dd75da56d39ec529fc5f6acf5c859ad.png

怎么样?感觉很爽吧?

不过普通用户默认是没有赋予提权权限的,还是需要请 root 帮忙配置

步骤如下


切换为 root 用户

打开 /etc/sudoers 这个文件

找到如下图所示区域,将需要提权的普通用户添加进去就行了

bf626cde3898a59b18cfabe20ec39f8.png


//root 身份下
# vim /etc/sudoers  //打开这个配置文件,找到上图区域进行修改就行了


当 提权 配置完成后,普通用户遇到权限拒绝的场景时,只需要 sudo 指令 ,然后输入当前普通用户的密码,就可以暂时借助 root 的身份无视权限完成指令了


注意: sudo 后,输入的是当前普通用户的密码,不需要输入 root 密码,这样就能做到保护 root 的情况下,执行指令了


📘总结


以上就是关于Linux工具:gcc/g++ 的全部介绍了,gcc/g++ 是一款优秀的编译器,它不仅可以编写C/C++ 代码,得益于强大的 GNU,它可以编写 绝大多数的后端语言代码(当然前端无缘,毕竟全是命令行);我们还学习了 库 的相关知识,知道了 动态库 与 静态库 的优缺点,还能通过 make 指令执行自动化任务,再配合上 sudo 提权,可以让我们的 Linux 开发效率大大增加


如果你觉得本文写的还不错的话,期待留下一个小小的赞👍,你的支持是我分享的最大动力!


如果本文有不足或错误的地方,随时欢迎指出,我会在第一时间改正

2433539e71b52f43a1677015d0d5a48.png

目录
相关文章
|
1月前
|
Linux 网络安全 数据安全/隐私保护
Linux 超级强大的十六进制 dump 工具:XXD 命令,我教你应该如何使用!
在 Linux 系统中,xxd 命令是一个强大的十六进制 dump 工具,可以将文件或数据以十六进制和 ASCII 字符形式显示,帮助用户深入了解和分析数据。本文详细介绍了 xxd 命令的基本用法、高级功能及实际应用案例,包括查看文件内容、指定输出格式、写入文件、数据比较、数据提取、数据转换和数据加密解密等。通过掌握这些技巧,用户可以更高效地处理各种数据问题。
110 8
|
1月前
|
Linux 编译器 开发工具
【Linux快速入门(三)】Linux与ROS学习之编译基础(Cmake编译)
【Linux快速入门(三)】Linux与ROS学习之编译基础(Cmake编译)
|
1月前
|
存储 安全 Linux
|
1月前
|
Linux Shell 数据安全/隐私保护
|
2月前
|
监控 Java Linux
Linux系统之安装Ward服务器监控工具
【10月更文挑战第17天】Linux系统之安装Ward服务器监控工具
70 5
Linux系统之安装Ward服务器监控工具
|
1月前
|
缓存 监控 Linux
Linux性能分析利器:全面掌握perf工具
【10月更文挑战第18天】 在Linux系统中,性能分析是确保软件运行效率的关键步骤。`perf`工具,作为Linux内核自带的性能分析工具,为开发者提供了强大的性能监控和分析能力。本文将全面介绍`perf`工具的使用,帮助你成为性能优化的高手。
162 1
|
1月前
|
缓存 监控 Linux
掌握Linux性能分析:深入探索perf工具
【10月更文挑战第26天】
71 1
|
关系型数据库 MySQL Linux
linux 的实用工具分享
做开发用Linux感觉比Windows在一些地方要好用(只是个人感觉,不想引战),在Linux中没有烦人的广告弹窗,没有动不动给你惊喜的Windows强制更新,而且Linux相对Windows要流畅,在低配的电脑上也很少卡顿.现在很多开发软件都有Linux版本,使用起来也算方便.当然,要是玩游戏等娱乐使用,还是Windows牛逼.我现在写代码基本都使用Linux.我用的Ubuntu18.04。
2072 0
|
1月前
|
监控 Linux
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
410 6