✨个人主页:****Yohifo
🎉所属专栏:****Linux学习之旅
🎊每篇一句:****图片来源
🎃操作环境:****CentOS 7.6 阿里云远程服务器
- Whatever is worth doing is worth doing well.- 任何值得去做的事情,都值得把它做好。
文章目录
📘前言
**书接上文,我们已经学习了
Linux
中的编辑器
vim
的相关使用方法,现在已经能直接在
Linux
中编写C/C++代码,有了代码之后就要尝试去编译并运行它,此时就可以学习一下
Linux
中的编译器
gcc/g++
了,我们一般使用
gcc
编译C语言,
g++
编译C++(当然
g++
也可编译C语言),这两个编译器我们可以当作一个来学习,因为它们的命令选项都是通用的,只是编译对象不同。除了编译器相关介绍外,本文还会库、自动化构建工具、提权等知识,一起来看看吧**
📘正文
📖gcc/g++ 命令
**在接下来的学习中,我们以
gcc
为例,因为两者选项都是通用的,所以也就相当于间接学习了
g++
,这个编译器上手还是很简单的,选项也不是很多**
注意:**如果命令失效,很有可能是没有下载
gcc/g++
,需要自行下载安装
gcc
与
g++
**
📃-o 目标文件
gcc 源文件
默认会将代码编译链接并生成可执行文件
a.out
,当然前提是代码没问题,所以这样看来编译一个文件还是很简单的
$ gcc 源文件 //直接编译源文件,生成默认可执行文件为 a.out
可能有的人不想让它生成默认的
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语言
预处理就像是过滤,会把代码进行检查删除,留下纯粹的C代码,方便后续进行转换
📃-S 编译
下面进入第二个步骤:编译
- 进行语法分析、词法分析、语义分析、符号汇总等,然后将合法的代码转为汇编代码
- 编译目的是生成汇编代码
- 编译后生成的文件后缀为
.s
编译阶段比较重要的一步就是符号汇总,它会各种符号汇总起来,方便后续符号表的形成,符号表用于各种函数间的相互调用
我们可以通过
-S
选项,使
gcc
在执行完编译阶段后就停下来,配合
-o
生成文件
test.s
$ gcc -S test.c -o test.s //可以直接从 test.c 开始执行,也可以从上一步中的 test.i 执行
📃-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 //可以通过软件,观察到符号表等信息
📃gcc 链接
下面是最后一步:链接
- 进行合并段表、将符号表进行合并和重定位等
- 将程序运行所需的各种函数链接起来,包括与库函数的链接,
Linux
中一般是动态链接,链接后生成可执行文件,此时的文件也是elf
的格式 gcc
默认生成的可执行文件为a.out
,我们可以指定生成任意文件
$ gcc test.c -o myfile //生成可执行文件为 myfile
$ gcc test.o -o myfile //继上一次生成的二进制文件执行链接,也是没有问题的
**以上就是本文关于
gcc/g++
的全部内容了**
📃小结
**关于各个命令选项可以巧记为
ESc
这是键盘上的一个键,忘记了可以看看**
**还有各个选项对应生成的文件后缀为
iso
**
下面还会介绍程序相关链接情况
📖库
**众所周知,每种编程语言都有属于自己的库,比如我们C语言中的
stdio
、
string
、
stdlib
等等标准库,当我们程序在调用库函数时,就是在调用标准库中的函数,而这些标准库都在
/usr/include
这个目录中,这个文件就是
Linux
中的C语言
动态库
;除了
动态库
外还有
静态库
**
📃动态库
动态库 即通过 动态链接 的库,动态库 又称 共享库,因为 动态库 中的内容是被所有程序共享的,简言之 动态库 中的代码只需要存在一份,程序需要使用时,直接通过对应位置调用就行了
Linux
中默认使用 动态链接 的方式,我们可以通过指令
ldd 最终生成的文件
来查看最终生成文件的链接情况
$ ldd 最终生成的文件 //查看文件的链接情况
libXXX.so是动态链接的标志
- 其中
lib
是前缀 .so
是后缀- 去掉前缀与后缀,就是最终调用的库
举例:
libc.so
去掉前缀与后缀,最终为
c
,可以看出文件最终调用的是C语言共享库,即 动态链接
动态链接 主要依赖不同函数在库中的位置信息进行调用,只有一份代码库,比较节省空间
我们还可以通过
file
命令查看文件详细信息
$ file 最终生成的文件 //查看文件的详细情况
这也验证了
Linux
默认使用 动态链接 的现象
类比记忆
动态库
就像是网吧(假设只有一家),那么全校的同学都可以去网吧中上网,还可以根据自己的喜好选择自己喜欢的机位,当然前提是你知道在哪个位置
📃静态库
除了 动态库 外,还有 静态库 ,采用 静态链接 的方式;静态链接 不同与 动态链接 共享的方式,如果程序调用 静态库 ,会将自己所需要的代码 拷贝至程序中 ,完成拷贝后,后续不需要再调用 静态库
如果想采用 静态链接 链接的方式编译程序,需要在编译时加上
-static
选项,当然前提是得有 静态库,没有的可以通过
yum install -y glibc-static
下载 静态库
当然我们也可以通过
ldd 最终生成的文件
查看是否为 静态链接
$ yum install -y glibc-static//下载静态库
$ gcc test.c -o myfile-static-static//采取静态链接的方式编译程序
$ ldd 最终生成的文件 //查看文件的链接方式
静态库 命名为 libXXX.a
lib
是前缀.a
是后缀- 去掉前缀与后缀,就是最终调用的库
我们也可以采用
file
命令查看详细信息
$ file 文件 //查看详细信息
静态链接 因为是直接将需要的代码拷贝到程序中,因此最终生成的文件会变大,比较占空间
因为这种方式很占空间,所以
Linux
中默认使用 动态链接 的方式
类比记忆
静态库
就像是把网吧里的电脑,买了一台同款的在自己寝室(调用某个函数),一台还好,如果买了很多台,寝室自然就没有空间了
📃优劣比对
动态库 和 静态库 各有优缺点,不然也不会同时存在两种库了
区别动态库静态库调用方式通过函数位置进行调用直接将需要的函数拷贝至程序中依赖性(运行时)需要依赖于动态库可以独立于静态库运行空间占用共享动态库中的代码,空间占用少拷贝代码会占用大量空间加载速度调用函数,加载速度慢直接运行,加载速度快
小结
动态库
- 优点 - 可以实现不同进程间的资源共享- 对于函数的升级只需要替换动态库文件,不需要重新编译程序- 可以控制是否加载动态库,不调用函数时就不加载
- 缺点 - 需要调用函数,加载速度较慢- 程序运行需要依赖动态库
静态库
- 优点 - 所需函数直接拷贝至程序中,运行速度快- 程序运行无需依赖库,便于移植
- 缺点 - 对于函数的升级,需要重新进行编译- 同一份代码可能出现重复拷贝的情况,浪费空间
📖自动化构建工具
**自动化构建工具可以帮助我们完成设置好的指令,指令为
make
,我们可以通过提前设置,实现源文件的快速编译**
📃Makefile 文件
要想使用
make
指令,就得先有
Makefile
文件,
Makefile
文件中主要编写任务,而任务由
依赖关系
+
依赖方法
构成
依赖关系
- 比如源文件为
test.c
,编译后生成的文件为myfile
,那么两者间的依赖关系
为myfile:test.c
这组依赖关系
我们可以写入Makefile
文件中
依赖方法
- 有了关系后,就要描述具体实现方法,比如上面那组
依赖关系
的依赖方法
为gcc test.c -o myfile
将依赖方法
也写入Makefile
文件中
完成上面两个内容的编写后,我们就得到了一个基本的自动化任务,输入
make myfile
即可编译
test.c
文件,生成
myfile
$ make myfile //执行自动化指令,编译 test.c 文件
注意:同一个自动化任务,执行成功后,如果相关文件最近没有发生改变,那么无法再次执行自动化任务
📃make 指令
**上面展示了如何编写
Makefile
文件并执行相关任务,使用了
make file
指令,并没有直接使用
make
指令,因为这个指令还是有些说法的**
单纯输入
make
指令时,默认执行
Makefile
中的第一个任务,当任务成功执行后,不再继续执行后续任务(一个
Makefile
文件中,可以有多个任务),由此可见,**单纯的
make
指令只会执行第一个自动化任务**
当我们编写好
Makefile
文件后,可以通过
make 任务名
调用任务,任务名就是
依赖关系
中的左侧名;也可以直接通过
make
调用第一个任务
📃任务刷新策略
前面说过,同一个方法如果成功执行过,在原文件最近修改时间没有发生变化时,无法再执行任务,这背后的原因是方法是否执行会先判断生成的目标文件是否为最新,如果为最新,就不再执任务
举例:**重复执行
make myfile
任务**
$ make myfile //第一次执行任务,成功
$ make myfile //第二次执行任务,失败,因为源文件最近没有被修改
**想要再次执行任务也很简单,对源文件做出修改,或者直接
touch
一下源文件就行了,两种行为都会修改文件的最近修改时间,使源目标文件不是最新时间**
📃.PHONY 伪目标
.PHONY
是
Makefile
文件中的一个关键字,意为对某某对象生成伪目标,这样就能在不对源文件进行修改的情况下,重复执行任务了
//Makefile 文件中.PHONY:myfile
在使用关键字
.PHONY
对目标进行修饰后,可以无视任务刷新策略,重复执行任务了
不过这有什么意义呢?
答:对于这种源文件来说,没有任何意义
.PHONY
这个关键字,一般是用来修饰
clean
任务,即清理解决方案,
Makefile
实现为
//Makefile 文件内.PHONY:clean
clean:
rm -r myfile
换个角度想想,当我们把生成的原目标文件清理后,再执行任务,生成目标文件是一件很合理的事,也完全符合任务刷新策略
由此来看,
.PHONY
也是很有用的
注意:**像
clean:
这种半缺失
依赖方法
是合理的,毕竟清理这个任务也不需要任何对象,只需要单纯的执行删除(清理)指令就行了**
📃补充
**
make
指令的工作原理是去
Makefile
文件中寻找任务执行,它的设计者为了确保普适性,创建
makefile
文件也是合法可用的**
也就是说,我们创建
make
指令的任务源文件时,可以创建为
Makefile
,也可以创建为
makeile
📖sudo 提权
权限,是一个让人又爱又恨的东西,它的安全性固然很重要,但有时候又太麻烦了,当我们普通用户想执行操作时,需要请
root
出马,比如最基本的下载软件指令,感觉有些小题大做了
为了解决这种不合理的现象,
Linux
中就有 **
sudo 提权
** 这个概念,**简单来说,就是暂时借助
root
的身份去完成某条指令**
$ sudo yum install -y sl //暂时提权下载软件
怎么样?感觉很爽吧?
**不过普通用户默认是没有赋予提权权限的,还是需要请
root
帮忙配置**
步骤如下
- 切换为
root
用户 - 打开
/etc/sudoers
这个文件 - 找到如下图所示区域,将需要提权的普通用户添加进去就行了
//root 身份下#vim/etc/sudoers //打开这个配置文件,找到上图区域进行修改就行了
**当
提权
配置完成后,普通用户遇到权限拒绝的场景时,只需要
sudo 指令
,然后输入当前普通用户的密码,就可以暂时借助
root
的身份无视权限完成指令了**
注意:**
sudo
后,输入的是当前普通用户的密码,不需要输入
root
密码,这样就能做到保护
root
的情况下,执行指令了**
📘总结
**以上就是关于
Linux
工具:
gcc/g++
的全部介绍了,
gcc/g++
是一款优秀的编译器,它不仅可以编写
C/C++
代码,得益于强大的
GNU
,它可以编写
绝大多数的后端语言代码
(当然前端无缘,毕竟全是命令行);我们还学习了
库
的相关知识,知道了
动态库
与
静态库
的优缺点,还能通过
make
指令执行自动化任务,再配合上
sudo 提权
,可以让我们的
Linux
开发效率大大增加**
如果你觉得本文写的还不错的话,期待留下一个小小的赞👍,你的支持是我分享的最大动力!
如果本文有不足或错误的地方,随时欢迎指出,我会在第一时间改正
…
相关文章推荐
Linux 权限理解和学习 (热榜文章,推荐食用)
Linux工具学习之【vim】(学习本文的必备文章)
听说Linux基础指令很多?这里都帮你总结好了
版权归原作者 Yohifo 所有, 如有侵权,请联系我们删除。