文章目录
- Linux 软件包管理器 yum
- Linux开发工具
- Linux编辑器-vim使用
- vim的基本概念
- vim下各模式的切换
- vim命令模式各命令汇总
- vim底行模式各命令汇总
- 批量化注释和批量化去注释
- vim简单的配置
- 解决一个小问题
- Linux编译器-gcc/g++作用
- gcc/g++ 语法
- 预处理
- 编译
- 汇编
- 链接
- 什么是函数库
- Linux调试器-gdb使用
- Linux项目自动化构建工具-make/Makefile
- Linux第一个小程序-进度条
Linux 软件包管理器 yum
Linux下安装软件的方式
在Linux下安装软件的方法大概有以下三种:
(1)下载到程序的源代码,自行进行编译,得到可执行程序。
(2)获取rpm安装包,通过rpm命令进行安装。(无法处理软件之间的依赖关系)
(3)通过yum进行安装软件。(常用)
yum全称为 Yellow dog Updater Modified,它是一个在线的软件安装命令。YUM是C/S架构,是RPM1的前端工具,依赖于RPM存在的。能够从指定的服务器自动下载RPM包并且安装,可以自动处理依赖性关系,并且一次安装所有依赖的软体包,无须繁琐地一次次下载、安装。
依赖关系:假如我要安装一个软件A而软件A又要依赖于软件B才能跑起来,万一软件B又要依赖其他软件,如果用下载源代码和获取rpm包就很麻烦处理了,因为他们处理不了软件之间的依赖关系只能需要下载哪个我们自己去下载。而我们用yum不用去处理这种复杂的关系。一步到位。
那么YUM是如何下载安装RMP包的呢?
但是上述这种机制,也存在着一些问题,万一服务器上的程序包或者依赖关系发生了变化,就会导致服务器和客户端本地的元数据不一致。想解决这个问题,可以在每次使用YUM的时候,都向服务器请求元数据。但是这种方式浪费带宽和时间,有的时候明明没有变化,但还是需要下载。
如何解决这种问题呢?
YUM服务器上有一个特殊文件,文件中记录了每一个元数据文件的校验码,实际上每次运行YUM的时候,都会向服务器请求这个文件(刚刚配置好YUM源的时候,本地没有这个文件,这时候肯定是要下载元数据的),并和本地元数据中的校验码文件进行对比,如果一致则代表元数据没有变化,本地元数据有效,否则就重新请求元数据。
查找软件包 yum list是罗列可下载的全部软件,这个时候可以配合那个管道和grep进行查询要下载的软件
比如要查询tree,可用以下指令
base,基本软件原,很稳定
epel,扩展软件源
可以理解成epel包含base,开发了更多的型新功能
安装软件和卸载软件前面指令那块说过了。这里再提醒一下
安装:yum -y install 软件名
卸载:yum -y remove 软件名
Linux开发工具
我们在 windows 中编写 C/C++ 程序时,常用的 VS2022,是一个集成开发环境,包含了很多工具包。而 Linux 下开发,大部分情况下都是使用的一个一个独立的工具。比如编写代码用 vim,编译代码用 gcc,调试代码用 gdb。
Linux编辑器-vim使用
vim的基本概念
我们讲解vim的三种模式(其实有好多模式,目前掌握这3种即可),
分别是命令模式(command mode)、插入模式(Insert mode)和底行模式(last line mode),各模式的功能区分如下:
正常/普通/命令模式(Normal mode):控制屏幕光标的移动,字符、字或行的删除,移动复制某区段及进入Insert mode下,或者到 last line mode
插入/编辑模式(Insert mode):只有在Insert mode下,才可以做文字输入,按「ESC」键可回到命令行模式。该模式是我们后面用的最频繁的编辑模式。
底行模式(last line mode):文件保存或退出,也可以进行文件替换,找字符串,列出行号等操作。
要查看你的所有模式:打开vim,底行模式直接输入:help vim-modes
vim下各模式的切换
【命令模式】->【插入模式】
(1)输入[ i ] :在当前光标处进入插入模式。
(2)输入[ o ]:在当前光标处新起一行进入插入模式。
(3)输入[ a ]:在当前光标的后一位置进入插入模式。
【命令模式】->【底行模式】
输入 [Shift+;] 即[ : ]。
【插入模式】或【底行模式】切换至【命令模式】
插入模式或是底行模式切换至命令模式都是直接按一下「Esc」键即可。
vim命令模式各命令汇总
【移动光标】
(1)按「k」:光标上移。
(2)按「j」:光标下移。
(3)按「h」:光标左移。
(4)按「l」:光标右移。
(5)按「$」:移动到光标所在行的行尾。
(6)按「^」:移动到光标所在行的行首。
(7)按「gg」:将光标移动到文本的最开头。
(8)按「Shift+g」:将光标移动到文本的最末尾。
(9)按「n+Shift+g」:移动到第n行行首。
(10)按「n+Enter」:当前光标向下移动n行。
(11)按「w」:光标从左到右,从上到下的跳到下一个字的开头。
(12)按「e」:光标从左到右,从上到下的跳到下一个字的结尾。
(12)按「b」:光标从右到左,从下到上的跳到上一个字的开头
【删除】
(1)按「x」:删除光标所在位置的字符。
(2)按「nx」:删除光标所在位置开始往后的n个字符。
(3)按「X」:删除光标所在位置的前一个字符。
(4)按「nX」:删除光标所在位置的前n个字符。
(5)按「dd」:删除光标所在行。
(6)按「ndd」:删除光标所在行开始往下的n行。
【复制粘贴】
(1)按「yy」:复制光标所在行到缓冲区。
(2)按「nyy」:复制光标所在行开始往下的n行到缓冲区。
(3)按「yw」:将光标所在位置开始到字尾的字符复制到缓冲区。
(4)按「nyw」:将光标所在位置开始往后的n个字复制到缓冲区。
(5)按「p」:将已复制的内容在光标的下一行粘贴上。
(6)按「np」:将已复制的内容在光标的下一行粘贴n次。
【剪切】
(1)按「dd」:剪切光标所在行。
(2)按「ndd」:剪切光标所在行开始往下的n行。
(3)按「p」:将已剪切的内容在光标的下一行粘贴上。
(4)按「np」:将已剪切的内容在光标的下一行粘贴n次。
【撤销】
(1)按「u」:撤销。
(2)按「Ctrl+r」:恢复刚刚的撤销。
【大小写切换】
(1)按「~」:完成光标所在位置字符的大小写切换。
(2)按「n~」:完成光标所在位置开始往后的n个字符的大小写切换。
【替换】
(1)按「r」:替换光标所在位置的字符。
(2)按「R」:替换光标所到位置的字符,直到按下「Esc」键为止。
【更改】
(1)按「cw」:将光标所在位置开始到字尾的字符删除,并进入插入模式。
(2)按「cnw」:将光标所在位置开始往后的n个字删除,并进入插入模式。
【翻页】
(1)按「Ctrl+b」:上翻一页。
(2)按「Ctrl+f」:下翻一页。
(3)按「Ctrl+u」:上翻半页。
(4)按「Ctrl+d」:下翻半页。
理解下面的这些指令也差不多了
vim底行模式各命令汇总
在使用底行模式之前,请记住先按「ESC」键确定您已经处于正常模式,再按「:」冒号即可进入底行模式。
行号指令
(1)「set nu」: 列出行号
(2)「set nonu」:取消行号
跳到文件中的某一行
在冒号后输入一个数字,再按回车键就会跳到该行了,如输入数字15,再回车,就会跳到文章的第15行。
查找字符
先按「/」键,再输入您想寻找的字符,如果第一次找的关键字不是您想要的,可以一直按「n」会往后寻找到您要的关键字为止。
保存文件退出vim
(1)「w」:保存文件。
(2)「q」:退出vim,如果无法离开vim,可在「q」后面跟一个「!」表示强制退出。
(3)「wq」:保存退出。
分屏指令
(1)「vs 文件名」:实现多文件的编辑。
(2)「Ctrl+w+w」:光标在多屏幕下进行切换。
执行指令
「!+指令」:在不退出vim的情况下,可以在指令前面加上「!」就可以执行Linux的指令,例如查看目录、编译当前代码等。
批量化注释和批量化去注释
批量化注释:先shift+v进入视图模式,hjkl进行区域选择(也可以n+shift+g进行选择),shift+i进入编辑模式然后直接加 //,再按ESC按键退出,一次不行的就按两次。
批量化去注释:先shift+v进入视图模式,hjkl进行区域选择,按两下ll选中//,再按d删除,
vim简单的配置
在目录 /etc/ 下面,有个名为vimrc的文件,这是系统中公共的vim配置文件,对所有用户都有效。
而在每个用户的主目录下,都可以自己建立私有的配置文件,命名为:“.vimrc”。
例如,/root目录下,通常已经存在一个.vimrc文件,如果不存在,则创建。切换用户成为自己执行 su ,进入自己的主工作目录,执行 cd ~,打开自己目录下的.vimrc文件,执行 vim .vimrc。
常用配置选项:
设置语法高亮: syntax on
显示行号: set nu
设置缩进的空格数为4: set shiftwidth=4
有些的还需要安装插件,这些配置太过的繁琐,大家可以直接复制下面这条指令去安装
curl -sLf https://gitee.com/HGtz2222/VimForCpp/raw/master/install.sh -o ./install.sh && bash ./install.sh
里面的配置很齐全哦。
解决一个小问题
就是如果你在插入模式,不小心退出了,下次进入遇到下图的问题
问题原因:编辑文件时断网或同一个文件在上一次编辑时未进行保存,则在下一次想要进行编辑时就会出现:
六个按钮的说明如下:
[O]pen Read-Only: 打开此档案成为只读档, 可以用在你只是想要查阅该档案内容并不想要进行编辑行为时。
(E)dit anyway:还是用正常的方式打开你要编辑的那个档案, 并不会载入暂存盘的内容。不过很容易出现两个使用者互相改变对方的档案等问题!
( R )ecover: 就是加载暂存盘的内容,用在你要救回之前未储存的工作。 不过当你救回来并且储存离开 vim 后,还是要手动自行删除那个暂存档喔!
(D)elete it: 你确定那个暂存档是无用的!那么开启档案前会先将这个暂存盘删除! 这个动作其实是比较常做的!因为你可能不确定这个暂存档是怎么来的,所以就删除掉他吧!
(Q)uit: 按下 q 就离开 vim ,不会进行任何动作回到命令提示字符。
(A)bort: 忽略这个编辑行为
其实我觉得如果没有重要的东西,一律用 rm + 双引号里面的内容。删除掉。
比如上面两个rm .bash_profile.swp
rm ~/.local/share/nvim/swap//%home%lwz%111%617%processbar.c.swp
注意:rm后面有个空格
Linux编译器-gcc/g++作用
gcc和g++分别是GNU的C和C++的编译器,gcc和g++在执行编译的时候一般有以下四个步骤:
预处理 --> 编译 --> 汇编 --> 链接
预处理(宏替换、去注释、头文件展开、条件编译)
编译(将c/c++变成汇编语言)
汇编(将汇编语言编译成二进制目标文件)
链接(形成可执行程序)
如果编译有问题在后面加上
gcc:-std=c99
g++:-std=c++11
gcc/g++ 语法
语法:gcc/g++ [选项] 要编译的文件 [选项] [目标文件]
常用选项:
(1)-E 只激活预处理,不生成文件,你需要把输出内容重定向到一个 .i 输出文件里面。
(2)-S 只进行预处理、编译阶段,并生成 .s 汇编文件,不进行汇编和链接。
(3)-c 只进行预处理、编译、汇编阶段,并生成 .o 目标文件,不进行链接。
(4)-o 指明要生成的文件,输出内容到一个输出文件中。
(5)-static 此选项对生成的文件采用静态链接。
(6)-g 生成调试信息。GNU 调试器可利用该信息。
(7)-shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统提供动态库。
(8)-O0、-O1、-O2、-O3 编译器的优化选项的 4 个级别,-O0 表示没有优化,-O1 为缺省值,- O3 优化级别最高。
(9)-w 不生成任何警告信息。
(10)-Wall 生成所有警告信息。
预处理
预处理功能主要包括宏替换、去注释、头文件展开、条件编译等。
预处理指令是以#号开头的代码行。
选项“-E”,该选项的作用是让 gcc 在预处理结束后停止编译过程。
选项“-o”是指目标文件,“xxx.i”文件为已经过预处理的C原始程序
gcc –E hello.c –o hello.i
编译
在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc 把代码翻译成汇编语言。
用户可以使用“-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。
-o选项是指目标文件,“xxx.s”文件为汇编语言
gcc –S hello.i –o hello.s
汇编
汇编阶段是把编译阶段生成的“.s”文件转成目标文件
可使用选项“-c”就可看到汇编代码已转化为“.o”的二进制目标代码
gcc –c hello.s –o hello.o
链接
◆在成功完成以上步骤之后,就进入了链接阶段。
◆链接的主要任务就是将生成的各个“xxx.o”文件进行链接,生成可执行文件。
◆gcc/g++不带-E、-S、-c选项时,就默认生成预处理、编译、汇编、链接全过程后的文件。
◆若不用-o选项指定生成文件的文件名,则默认生成的可执行文件名为a.out。
gcc hello.o –o hello
注意: 链接后生成的也是二进制文件
什么是函数库
我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实“printf”函数的呢?
系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,而这也就是链接的作用。
函数库一般分为静态库和动态库两种。
静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为“.a”
动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”
如前面所述的 libc.so.6 就是动态库。gcc 在编译时默认使用动态库。完成了链接之后,gcc 就可以生成可执行文件,如下所示。 gcc hello.o –o hello
gcc默认生成的二进制程序,是动态链接的,这点可以通过 file / ldd 命令验证
语法:ldd [filename]
功能:可查看可执行文件的库依赖关系。
语法:file [filename]
功能:可以查看可执行文件的信息和类型。
动态链接
优点:不需要把相关库中的代码拷贝到可执行程序中,编译效率高,程序运行起来后,需要用到哪个库,再把哪个库加载到内存中,边运行边加载。
缺点:万一有库丢失了,将直接导致程序无法正常运行
静态链接:
优点:不依赖第三方库,程序的可移植性较高。
缺点:浪费空间。
Linux调试器-gdb使用
程序的发布方式有两种
debug 模式(在生成可执行程序的时候,会加入调试信息,可调试)
release 模式(没有调试信息,不可被调试)
Linux 中 gcc/g++ 编译生成的可执行程序,默认是 release 模式
要使用gdb调试,必须在源代码生成二进制程序的时候, 加上 -g 选项
因为debug在生成可执行程序时,会加入调试信息,所以它的大小肯定会比release版本要大。
这就是加入的调试信息哦
gdb命令汇总
【进入gdb】
指令: gdb 文件名
【调试】
(1)「run/r」:运行代码(启动调试)。
(2)「next/n」:逐过程调试。
(3)「step/s」:逐语句调试。
(4)「until 行号」:跳转至指定行。
(5)「finish」:执行完当前正在调用的函数后停下来(不能是主函数)。
(6)「continue/c」:运行到下一个断点处。
(7)「set var 变量=x」:修改变量的值为x。
【显示】
(1)「list/l n」:显示从第n行开始的源代码,每次显示10行,若n未给出则默认从上次的位置往下显示.。
(2)「list/l 函数名」:显示该函数的源代码。
(3)「print/p 变量」:打印变量的值。
(4)「print/p &变量」:打印变量的地址。
(5)「print/p 表达式」:打印表达式的值,通过表达式可以修改变量的值。
(6)「display 变量」:将变量加入常显示(每次停下来都显示它的值)。
(7)「display &变量」:将变量的地址加入常显示。
(8)「undisplay 编号」:取消指定编号变量的常显示。
(9)「bt」:查看各级函数调用及参数。
(10)「info/i locals」:查看当前栈帧当中局部变量的值。
【断点】
(1)「break/b n」:在第n行设置断点。
(2)「break/b 函数名」:在某函数体内第一行设置断点。
(3)「info breakpoint/b」:查看已打断点信息。
(4)「delete/d 编号」:删除指定编号的断点。
(5)「disable 编号」:禁用指定编号的断点。
(6)「enable 编号」:启用指定编号的断点。
【退出gdb】
「quit/q」:退出gdb。
Linux项目自动化构建工具-make/Makefile
重要性
会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力
一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。
make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建。
依赖关系和依赖方法
当你的工程当中有多个源文件的时候,应该如何进行编译生成可执行程序呢?
首先,我们可以直接使用gcc指令对多个源文件进行编译,进而生成可执行程序。
但进行多文件编译的时候一般不使用源文件直接生成可执行程序,而是先用每个源文件各自生成自己的二进制文件,然后再将这些二进制文件通过链接生成可执行程序。
因为若是直接使用源文件生成可执行程序,那么其中一个源文件进行了修改,再生成可执行程序的时候就需要将所有的源文件重新进行编译链接。而若是先用每个源文件各自生成自己的二进制文件,那么其中一个源文件进行了修改,就只需重新编译生成该源文件的二进制文件,然后再将这些二进制文件通过链接生成可执行程序即可。
注意:编译链接的时候不需要加上头文件,因为编译器通过源文件的内容可以知道所需的头文件名字,而通过头文件的包含方式(“尖括号”包含和“双引号”包含),编译器可以知道应该从何处去寻找所需头文件。
但是随着源文件个数的增加,我们每次重新生成可执行程序时,所需输入的gcc指令的长度与个数也会随之增加。这时我们就需要使用make和Makefile了,这将大大减少我们的工作量。
步骤一: 在源文件所在目录下创建一个名为Makefile/makefile的文件。
步骤二: 编写Makefile文件。
Makefile文件最简单的编写格式是,先写出文件的依赖关系,然后写出这些文件之间的依赖方法,依次写下去。
编写完毕Makefile文件后保存退出,然后在命令行当中执行make指令便可以生成可执行程序,以及该过程产生的中间产物。
Makefile文件的简写方式:
- $@:表示依赖关系中的目标文件(冒号左侧)。
- $^:表示依赖关系中的依赖文件列表(冒号右侧全部)。
- $<:表示依赖关系中的第一个依赖文件(冒号右侧第一个)。
例如以上Makefile文件可以简写为:
说明: gcc/g++携带-c选项时,若不指定输出文件的文件名,则默认输出文件名为xxx.o,所以这里也可以不用指定输出文件名。
make工作原理
- make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
- 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“hello”这个文件,并把这个文件作为最终的目标文件。
- 如果hello文件不存在,或是hello所依赖的后面的hello.o文件的文件修改时间要比hello这个文件新(可以用 touch 测试),那么,他就会执行后面所定义的命令来生成hello这个文件。
- 如果hello所依赖的hello.o文件不存在,那么make会在当前文件中找目标为hello.o文件的依赖性,如果找到则再根据那一个规则生成hello.o文件。(这有点像一个堆栈的过程)
- 当然,你的C文件和H文件是存在的啦,于是make会生成 hello.o 文件,然后再用 hello.o 文件声明make的终极任务,也就是执行文件hello了。
- 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
- 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。
- make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。
项目清理
在我们每次重新生成可执行程序前,都应该将上一次生成可执行程序时生成的一系列文件进行清理,但是如果我们每次都手动执行一系列指令进行清理工作的话,未免有些麻烦,因为每次清理时执行的都是相同的清理指令,这时我们可以将项目清理的指令也加入到Makefile文件当中。
像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行。即命令——“make clean”,以此来清除所有的目标文件,以便重编译。
◆但是一般我们这种clean的目标文件,我们将它设置为伪目标,用 .PHONY 修饰,伪目标的特性是,总是被执行的。
Linux第一个小程序-进度条
回车和换行的概念
回车就是指光标移动到本行的行首
换行则是指光标移动到下一行当前位置
\r 表示回车
\n 表示回车+换行
键盘enter按键其实就是换行+回车,看形状有没有那个感觉呢
行缓冲区概念
大家先想一想以下三个代码的运行结果是怎样的?大家下去自己试试哦
这里我们要告诉大家的是
第一个是先输出字符串hello Makefile!然后休眠3秒之后结束运行
第二个是先休眠3秒然后输出字符串hello Makefile!之后结束运行
第三个和第一个的结果是一样的
前面两个代码的结果就证明了行缓冲区的存在。
缓冲区的刷新三种方法:
加 \n (换行)
加 fflush(stdout)
程序结束
显示器对应的是行刷新,即当缓冲区当中遇到’\n’或是缓冲区被写满才会被打印出来,而在第二份代码当中并没有’\n’,所以字符串hello Makefile!先被写到缓冲区当中去了,然后休眠3秒后,直到程序运行结束时才将hello Makefile!打印到显示器当中。
写一个简单的倒计时
虽然我们这里 i 是整型,但实际上打印到显示器上,是一个一个的字符。
比如 int i = 100,占 4 字节,使用 printf 打印到显示器上,是 6 个字符,占 6 字节。
printf 格式化输出,实际上就是把这个内存级的整型数据转换成显示器可以显示的字符型的数据。
scanf 格式化输入,实际上就是把键盘敲下的一个个字符型的数据转换成了一个内存级的整型数据。
文件分为二进制文件和文本文件,二进位文件在内存中是什么样子,写到文件中也就是什么样子,而文本文件写入到设备(文件)中时,是需要做转换的,比如显示器设备(也是一种文件),显示器是给人看的,所以它一定不是二进制文件,而是文本文件,只要是文本文件,必须要将所要显示的数据转换成人所能识别的一个一个的字符型数据。所以键盘和显示器设备(文件),统称为字符设备,体现在输入时是字符,输出时是字符。
你可以试着去掉%3d里面那个3,看看会有什么结果,在试着理解上面一段话。
好好理解哦
实现进度条
回顾一下memset()函数吧
void *memset(void *str, int c, size_t n)
解释:复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。
作用:是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法
头文件:C中#include<string.h>,C++中#include