文章目录
软件安装
- 大家平时使用手机的时候因该也没少下载过APP吧,如果我们想要使用具有某个功能的软件,我们就需要重应用商店里去下载对应的软件。
我们在Linux上下载软件也类似,需要通过一个类似于应用商店的东西去进行下载。我们平时比较常见的就是yum
了。它主要应用在Fedora,RedHat,Centos等Linux的发行版,在我前面几期博客里面也有用到这个指令。
yum指令
类似与应用商店,yum提供了查找、安装、删除某一个、一组甚至全部软件包的命令,使用十分方便。但对于普通用户,我们不能够直接使用yum,需要使用sudo
进行提权。但对应root用户来说就没有这么多限制了。
1、查看软件列表
2、下载软件
3、卸载软件
查看软件列表;
通过命令
yum list
我们可以查看yum里面的软件。
可以看到巨多软件,如果一个一个去查找实在是太麻烦了。不过,我们可以通过使用管道来过滤相应的信息。之后我们会用到一款名叫vim
的编辑器,它是我们在Linux上敲代码的基础,可以提高我们在Linux上写代码的体验。输入指令yum list | grep vim
即可可它进行查找。
其中第一行是软件包名称,版本号以及主机平台等信息。
第二行是操作系统的发行版本。el7_9就是Centos7.9
第三行是软件包来源。
下载软件
在查找到了软件之后就可以直接进行下载了。还是以vim为例,输入指令
yum -y install yum
(普通用户要提权)进行下载,就会自动下载好最近版本。
其中-y
的作用是忽略系统的提示,如果不带-y
系统还会问你是否确认安装,就比较麻烦。所以一般下载软件包的时候都会带上它。(卸载软件的时候同理)
卸载软件
与下载软件类似,输入指令yum -y remove 软件名
就可以对对应软件进行卸载。同样的对于普通用户来说需要使用sudo
进行提权。
vim编辑器
- vim是一个基于Vi的功能强大、高度可定制的多模式文本编辑器,它在Vi的基础上改进和增加了很多特性。简单来说就是我们平时用来写代码的东西。在之前,我们使用的是nano对文本进行编辑,但vim的功能比之nano强大太多。
但vim只是一个编辑器,也就是说它只能对文件进行编辑,不能进行调试和编译等功能。这是与我们平时所使用的vs不同的地方。不过我们可以通过对vim进行配置从而使我们在vim上写代码的体验接近于vs。
vim常用模式
在vim编辑器中有许多模式,我们也能在不同模式下实现各种功能。熟练各种模式的使用以及切换也就成了使用好vim的第一个基本条件了。一般vim共分为五种模式,分别为命令模式、底行输入模式、插入模式、替换模式和视图模式。
常见模式 | 功能 |
---|---|
命令模式 | 可使用方向键(上、下、左、右键)或 k、j、h、i 移动光标的位置,还可以对文件内容进行复制、粘贴、替换、删除等操作(默认打开vim就是命令模式)。 |
底行输入模式 | 输入特定的指令用来执行对应的操作。(常见的如保存文件,退出vim编辑器等) |
插入模式 | 对文件执行写操作,类似于在Windows系统的文档中输入内容。 |
替换模式 | 对文件执行替换操作。 |
视图模式 | 用于选择文本,类似于用鼠标单击和拖动的行为。 选择文本使命令仅适用于选择,例如复制,删除,替换等。 |
各模式的切换
了解了vim的相关模式之后,各种模式之间的切换又是如何实现的呢?
Tips:
1.如果你不知道你自己在什么模式下,无脑Esc。
2.要实现模式的切换必须要经过命令模式。
3.退出的时候,一般都是先保存,再退出。
4. vim中操作,不要用鼠标和鼠标的滑轮。
vim指令集
vim指令有许多,而且对应不同的模式也会有不同的指令。这里按照使用范围,以及使用功能的不同来对指令进行整理。
模式切换指令
shift :
进入底行输入模式,光标会移动到底行。
i(I)
进入输入模式,i是从目前光标所在处输入,I为在目前所在行的第一个非空格符处开始输入。
shift r(R)
进入替换模式r只会取代光标所在的那一个字符一次,R会一直取代光标所在的文字,直到按下Esc为止。
shift v(V)/Ctrl + v
进入视图模式。v/V进入的行可视化模式,文本选择是以行为单位的,Ctrl + v进入的字符可视化模式,文本选择是以字符为单位的。
以上四个指令只能在命令模式下使用
Esc
从当前模式返回到命令模式中。
文本操作指令
yy
复制光标所在的那一行。
nyy
n 为数字。复制光标所在的向下 n 行,例如10yy,就是复制从光标开始向下10行。
y1G
复制游标所在行到第一行的所有数据。
yG
复制游标所在行到最后一行的所有数据。
p
在光标所在行的下一行开始粘贴复制、剪切的内容;
np
n为数字,在光标所在行的下一行开始粘贴复制、剪切的内容,把复制、剪切内容粘贴n次。
P
在光标所在行的上一行开始粘贴复制、剪切的内容;
nP
n为数字,在光标所在行的上一行开始粘贴复制、剪切的内容,把复制、剪切内容粘贴n次。
dd
剪切光标所在的那一整行。
ndd
n为数字,剪切光标所在行以内向下n行。
d1G
删除光标所在到第一行的所有数据。
dG
删除光标所在到最后一行的所有数据。
x
删除光标后的一个字符。
nx
n为数字,删除光标后的n个字符。
X
删除光标前的一个字符。
nX
n为数字,删除光标前的n个字符。
r
替换光标所在位置的字符;在执行该命令后,我们需要输入需要替换的字符。
nr
替换光标开始的位置的n个字符。
shift ~
将光标所在位置的字母进行大小写进行转换。
u
撤销刚刚操作。
Ctrl+r
对撤销进行撤销。
光标移动指令
h
光标向左移动一个字符。
j
光标向下移动一个字符。
k
光标向上移动一个字符。
l
光标向右移动一个字符。
如果想要进行多次移动的话,例如向下移动10行,可以通过 “10j” 或 "10↓"来实现。
G
将光标移动到全文最后一个字符的位置。
nG
n为数字,将光标移动到全文第n行。
gg
将光标移动到全文开头。
$
将光标定位到本行开头位置。
^
将光标定位到本行末尾位置。
H
光标移动到这个屏幕的最上方那一行的第一个字符。
M
光标移动到这个屏幕的中央那一行的第一个字符。
L
光标移动到这个屏幕的最下方那一行的第一个字符。
w
光标以单词为单位开始从左往右移动,字母为一组单词、非字母为一组单词、空格直接跳过,每次都移动到单词开头位置。
b
光标以单词为单位开始从右往左移动,字母为一组单词、非字母为一组单词、空格直接跳过,每次都移动到单词开头位置。
nw
以光标所在单词为基准向后跳n个单词。
nb
以光标所在单词为基准向前跳n个单词。
n<space>
n 表示数字,按下数字后再按空格键,光标会向右移动这一行的第n个字符。
Ctrl+f
向前翻一页。
Ctrl+b
向后翻一页。
Ctrl+d
向前翻半页。
Ctrl+u
向后翻半页。
底行输入模式下的指令
w
保存数据。
q
退出vim,但退出前必须先进行保存,否则不能退出。
!
强制执行当前命令。
w 文件名
将编辑的数据储存到另一个文件中。
vs 文件名
再打开一个文件,新打开的文件会以分屏形式展示,原打开的文件不会关闭,光标所在为当前所操作文件。可以通过Ctrl+ww
来在不同文件之间切换。
%s/aaa/bbb/g
从第一行到最后一行寻找aaa字符串,并将该字符串取代为bbb。
n
n为数字,把光标定位到第n行开头。
/key
从光标位置向后搜索xxx,搜索到后定位到行。
?key
从光标位置向前搜索xxx,搜索到后定位到行。
set nu
显示行号,设定之后,会在每一行的前缀显示该行的行号。
set nonu
取消行号显示。
视图模式下的指令
d
删除选中的部分文本。
D
删除选中部分所在的行。
y
复制选中部分。
p
粘贴选中部分到光标后。
P
粘贴选中部分到光标前。
u
将选中部分中的大写字符全部改为小写字符。
U
将选中部分中的小写字符全部改为大写字符。
I
进入插入模式与从命令模式输i进入插入模式效果一样。
在视图模式下,我们可以实现对代码的批量注释。 这也是最常用到的地方。
首先以Ctrl + v
的方式进入视图模式。
进入视图模式后通过“h、j、k、l”移动光标选定操作区域。然后输入I
进入插入模式,输入//
,最后再按Esc返回命令模式就可以在选中区域前面成功插入//
了。
当然也可以通过同样的方式来取消注释。
选中之后直接按d即可完成删除。
vim的配置
- vim之所以被广大程序员喜欢,有一大原因就是他可以通过配置来极大地提高程序员的代码体验。相当于量身定制的编辑器。那么vim是如何进行配置的呢?
首先,在目录/etc/里,有个名为vimrc的文件,这就是vim的配置文件,但这个配置文件是对所有用户都适用的。同样的,你也可以单独配置自己用户的vim。
首先,找到你想配置的用户的根目录,然后在里面创建一个.vimrc
文件。这就是你这个用户的vim配置文件,然后你就可以通过vim打开它对它进行配置了。
这样配置出来的vim并不会对其他用户的vim产生影响。
至于如何配置,举个简单的例子。你在配置文件中输入set nu
然后保存,等你再次进入vim的时候就可以看到他默认显示了行号。
其他的配置大家可以去网上进行搜索。
当然,如果你不想手动配置,也可以进行自动配置。
输入指令curl -sLf https://gitee.com/HGtz2222/VimForCpp/raw/master/install.sh -o ./install.sh && bash ./install.sh
即可自动配置(这是别人配置好的我们拿来用)。
gcc/g++编译器
在通过vim编写好代码后,就要运行代码了。但我们说过,vim只是一个编辑器,并不能对代码进行编译。所以,我们要通过编译器对代码进行处理从而形成可执行程序。编译器以gcc为例,如果不能执行gcc
指令,则是因为还没有下载好gcc,可以通过指令yum -y install gcc
进行下载。gcc是c语言编译器,专门用来编译C语言代码,不能用来编译C++代码。g++是C++编译器,是用来编译C++代码的,但是它也可以用来编译C语言代码。
对于代码的编译,它一般可以分为四个阶段,即预处理、编译、汇编和链接阶段。 与vs不同的是,通过gcc编译器,我们可以详细地看到每一个阶段执行后的结果。
预处理阶段
代码在这个阶段之后会生成一个.i
文件即预处理文件。而这个阶段会对C语言代码进行一些文本替换方面的操作,例如宏展开、文件包含、删除部分代码等,最后输出预处理文件。
输入指令
gcc -E 代码文件名.c -o 预处理文件名.i
即可实现代码的预处理。
-E
选项是让gcc在对程序进行预处理之后就停止。
-o
选项是把它输出到某个指定文件。
在不采用-o
选项时,它会默认输出到标准输出流,即屏幕上。
通过-o
选项我们则可把它输出到一个指定的文件中。
两者进行对比结果:
我们可以发现进行预处理后,我们之前的注释部分被删掉了,然后预处理文件还多出了一些文件的包含。这里提一嘴,注释删除是在宏替换之前实现的。
编译阶段
这个阶段会生成.s
文件的汇编代码。在编译阶段中,gcc首先要检查代码的规范性以及是否有语法错误等,平时我们语法错误就是在这个阶段报错的,用来确定代码的功能,然后进行符号汇总。在功能无误后,gcc会把预处理阶段生成的预处理文件编译成汇编代码。
输入指令
gcc -S 代码文件名.c -o 汇编代码名.s
即可实现代码的编译。
也可以通过指令gcc -S 预处理文件名.i -o 汇编代码名.s
来实现。
-S
选项是让gcc在对程序进行编译之后就停止。
-o
选项是把它输出到某个指定文件。
这里用法和预处理阶段相似,直接上编译后的结果。
这里看到它给我报错了,错误的地方也指明了,这里我们把<stdio>
改为# <stdio.h>
之后就能成功执行编译了。
打开test.s就是它的汇编代码了。
简简单单一个Hello word!
竟然变成了这么复杂的东西。大家感兴趣的话可以去分析一下这个汇编代码,笔者就不过多说明了。
汇编阶段
这个阶段会把汇编代码转换成二进制.o
文件。在汇编阶段中,编译器会生成符号表,还会把对应的汇编文件转换成二进制文件。而符号表可以理解为地址,但这些地址还是无效地址。
输入指令
gcc -c 代码文件名.c -o 二进制文件名.o
即可实现代码的编译。
也可以通过指令gcc -c 汇编代码名.s -o 二进制文件名.o
来实现。
-c
选项是让gcc在对程序进行汇编之后就停止。
-o
选项是把它输出到某个指定文件。
这个阶段我就完全看不懂了好吧,大家来感受下。
是不是很像平时遇到的乱码?因为是二进制文件,我们看不懂但电脑是看得懂的。
链接阶段
这个阶段会把我们汇编阶段生成的二进制文件转换成可执行程序,也就是编译的最后一步了。在这个阶段中,会将二进制文件链接成一个可执行的命令,主要是把分散的数据和代码收集并合成一个单一的可加载并可执行的的文件。在这个过程中,符号表会进行重定位,同时进行库的链接。这里的符号表重定位可以理解为把汇编阶段的无效地址转化为有效地址。
举个例子,printf函数只有在预编译中包含进的”stdio.h”中才有该函数的实现以及声明。而链接就是把printf这个函数同这个”stdio.h”链接在一起,这样计算机就能知道printf是如何实现的了。其本质就是把一步步程序同库链接在一起的过程。而库也是有类别的,这个我待会儿会详细讲。
输入指令
gcc 代码文件名.c -o 可执行文件名
即可实现代码的链接。
也可以通过指令gcc 二进制文件名.o -o 可执行文件名
来实现。
-o
选项是把它输出到某个指定文件。
这一步生成的文件直接输入就可以运行了。
这里可以看到报错了,原因是printf
被输成了prinft
导致了没法把prinft
进行链接。这不是废话么,函数是名都变了,怎么会找的到。
那么,为什么不在编译阶段进行报错呢?这是因为编译阶段检查的是语法是否正确,即你;
是否正确使用等int
是否写成了itn
这种语法上的错误。但printf本质上是一个函数,它的实现是在库里面的,在没连接之前是不知道它错误的,因为函数名是可以自己设置的。只有链接的时候找不到了才会报错。
至于像栈溢出等错误这里也是检测不出来的,得执行的时候才会报错。
改正之后即可正常编译,输入./test
就可执行程序。
这里说明一下,如果不使用-o
进行重定向的话,默认会生成a.out
这个可执行程序。
函数库
在函数编译的链接阶段,我们就提到了库。链接阶段是把语言与函数库链接起来的过程,那么什么是函数库呢?
所谓库,它指的是一个可供使用的各种标准程序、子程序、文件以及它们的目录等信息的有序集合。粗俗地讲,一个程序的核心代码,一般都会被封装在一个你可以使用,但不能看到是如何实现的文件中,而这个文件也就是所谓的库文件。这也是程序员卖出程序也不怕代码泄露的原因。
而linux下库主要处于/lib
和/usr/lib
目录中。其中/lib
指系统运行相关的库文件,/usr/lib
一般指第三方软件的库文件。
就比如我们平时最常用的
stdio.h
,它的位置就在/usr/include/
。
而按照库在代码中被载入的时刻不同,我们又可以把库分为动态库和静态库(共享库)。而所谓静态和动态就是指的链接的过程。
说了呢么多,如何查看文件所连接的库呢?
输入指令ldd 可执行程序
即可。
也可以通过file 可执行程序
进行查看。
可以看到我们不对编译进行设置的话默认都是使用的动态库。这也一定程度上说明了,在我们平时编译的时候,动态库是基本上必不可少的。
我们打开这个动态库,就可以发现它是一个链接。
其中ld-2.17.so
就是链接的远程的库。
静态库
在Linux中,一般静态链接库的名字形式为libxxx.a
,其中lib
是前缀,后缀是.a
。在Windows中以静态库的后缀是.lib
。之所以叫做静态库是因为在链接阶段,会将汇编生成的.o
文件与引用到的库一起链接打包到可执行文件中。
也就是说库会打包到可执行文件中去。既然这样,那么使用静态链接生成的可执行文件即使你把对应的库删除之后依旧能运行。
在介绍库的时候我就说了,一般编译的时候是默认链接的动态库,那么如何使它链接静态库呢?
输入指令gcc 代码文件.c -o 执行程序 -static
即可。
但是如果报错的话说明还没有安装静态库,输入指令(sudo) yum install -y glibc-static
进行安装(普通用户要进行提权),这个是c语言的静态库。
c++静态库对应的指令是(sudo) yum install -y libstdc++-static
。
同时我们吧静态链接生成的文件和动态链接的文件进行对比。
两者在大小上竟然差了百倍!!!
那么静态库的特点也就显而易见了:
- 静态库对函数库的链接是在编译时期就完成的。
- 程序在运行时不依赖于任何库,完全独立运行,移植方便。
- 占用大量的内存、和磁盘空间,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。
- 代码的维护比较困难,因为即使修改一点代码,程序就得重新进行编译。
动态库
在Linux中,一般动态链接库的名字形式为libxxx.so
,其中lib
是前缀,后缀是.so
。在Windows中以动态库的后缀是.dll
。
动态库也就是共享库,因为他是云端上的,而动态链接就是把文件同云端的库链接起来,所以即使更新了库,也不需要对程序进行重新编译。
动态库的特点有:
- 动态库和动态链接:链接的时候,如果是动态链接,找到动态库,拷贝动态库中的我所需要的代码的地址到我自己的可执行程序中相关的位置。
- 动态链接成功:我们的程序,还是依赖动态库,一旦动态库缺失,我们的程序便无法运行!
- 因为可以做到被大家共享方法,所以真正的实现永远都是在库中,程序内部只有地址,比较节省空间。
- Linux下gcc编译的执行文件默认是ELF格式,不需要初始化入口,亦不需要函数做特别的声明,编写比较方便。
自动化编译工具Makefile/make
上面我们只是介绍了一个单独文件的编译方法。但在我们平时写代码的时候难免会有许多不同的文件来实现一个需求。举个例子,你单独添加的头文件和函数文件这些文件都是这个代码的原文件,他们用来共同实现一个项目,按照我们上述所讲就必须用gcc命令将该项目下的所有源文件、及头文件进行单独编译,然后在将这些汇编生成的.o文件链接在一起生成可执行程序。
这样可以说是十分麻烦。但在我们平时所使用的vs中就没有这种烦恼,因为vs会自动把所有的原文件链接成一个可执行程序。那么,我们能不能在Linux上也通过一种方法是使我们把许多不同的原文件一键连接到一起了?答案就是make指令。
make是一个命令工具,是一个解释makefile中指令的命令工具,该指令读入一个名为makefile的文件,然后执行这个文件中指定的指令。
而建造一个名为makefile的文件再通过mdke指令进行编译就能得到多个源文件链接而成的可执行程序了。
首先在本项目的目录下创建一个名为makefile
或者Makefile
的普通文件,然后编辑makefile/Makefile文件。
具体编写格式如下:
目标文件名:所有源文件名
Tab 对应指令
如果有多个源文件意义写上就行了,然后输入make执行指令,就能生成对应的原文件了。
当我们再次make一下时,这个时候就会报错了,是因为当前目录下已经存在目标文件了,且目标文件没有更新过,这个时候就不会执行指令。
这是因为我没有对源代码进行修改,所以就不会执行指令。一旦源代码发生了变动,就会重新用源文件生成新的执行文件并覆盖原执行程序。
同时,我们也能通过make指令来对生成的执行程序进行别的指令。
clean
就是我设置的一个新的指令,这种在makefile文件中不会生成新的clean
文件的指令我们称之为伪目标。而伪目标通常用来表示一些特定的操作。
也就是说,我们在直接make的时候是默认执行第一条指令的。但我们可以通过特定的指令如上图中的make clean
来执行特定的指令。
在知道了什么是伪目标后,那么怎么让Linux知道它是一个伪目标呢?
在上面的内容中,我们知道,当目标文件不会被生成的时候,目标文件名就成了一个伪目标。所以在执行make
的时候,Linux还得去判断这个指令是否是一个伪目标,这样来看就有些冗余了。那么,怎么让Linux知道它就是一个伪目标而不需要自己去判断呢?
我们可以通过.PHONY:伪目标
来进行声明。
这样的好处还有一个就是即使在make的文件夹中存在clean文件也能顺利执行clean指令。当然如果你定义上图中的exe
为伪目标,在执行的时候即使没有修改原文件也会生成新的执行程序覆盖以前的执行程序。也就是说虽然Linux以为他是伪目标,就按照伪目标的方式执行,但在执行指令的时候还是会生成执行程序,如果原来存在同名文件就会覆盖。
gdb调试器
gdb是Linux下常用的程序调试器。通常程序中出现的语法错误在编译阶段发现,但逻辑错误就只能通过修改代码来解决了。这个时候我们往往就需要用到gdb对程序进行调试。
gdb调试器主要功能有:
- 程序启动时,可以按照我们自定义的要求运行程序,例如设置参数和环境变量。
- 可使被调试程序在指定代码处暂停运行,并查看当前程序的运行状态(例如当前变量的值,函数的执行结果等),即支持断点调试。
- 程序执行过程中,可以改变某个变量的值,还可以改变代码的执行顺序,从而尝试修改程序中出现的逻辑错误。
如果还没有安装gdb,输入指令
yum -y install gdb
即可。
用过vs都知道,可执行程序分为debug版本和release版本两种,而只有debug版本才能进行调试。但我们gcc默认生成的是release版本,所以我们需要通过-g
指令来生成debug版本。
先跑一下:
其中免责声明可以通过-q
指令来进行屏蔽。
gdb指令
调试指令 | 功能 |
---|---|
list(l) | 显示代码的内容,包括各行代码所在的行号。 |
l + 行号 | 从指定行开始展示代码内容。 |
run(r) | 从头开始运行程序,会在遇到的第一个断点处停下来。后续如果还有断点的话,run就不会停下来。如果整个程序没有断点的话就会跑完整个程序。 |
break(b)+ 行号 | 在当前文件的指定行设置断点。 |
continue (c) | 当程序在某一断点处停止后,用该指令可以继续执行,直至遇到断点或者程序结束。 |
next (n) | 逐过程调试,不会进入函数内部,类似于vs中的F10。 |
step(s) | 逐语句调试,会进入函数内部,类似于vs中的F11。 |
clear + 行号 | 删除指定行的断点。 |
delete(d)+ 编号 | 删除指定号码的断点,该号码不是行号,而是调试器为每个断点单独设置的编号。 |
d breakpoints | 删除所有断点。 |
disable + 编号 | 禁用某个断点。(不是删除) |
enable + 编号 | 启用某个断点。 |
info b | 查看当前设置的断点信息。 |
print(p)+ 变量 | 打印指定变量的值。 |
display + 变量 | 监视某个变量的信息。(会一直显示) |
undisplay + 变量 | 取消监视某个变量。 |
d display | 删除全部监视变量。 |
info display | 查看监视变量的情况。 |
quit (q) | 退出gdb。 |