祖师爷敬上!!!
本文主要讲解一些关于Linux基础工具的使用;
1、在Linux环境下,下载和安装软件,一个通常的办法就是下载对应软件的源代码,下载下来过后,自己手动编译获得可执行程序!
2、但是这样的操作似乎有些麻烦,于是有些大佬就直接帮我们编译好了,做成了软件包,我们直接去获取这个软件包也能得到可执行程序,相比于上面的方法,我们省去了自己手动编译源代码的操作,节省了大量的时间;
3、软件包和软件管理器之间的关系就像是:“APP”与“手机应用商店的关系”,再具体化一点就是:“王者荣耀”与“小米应用商店”的关系,我们直接去“小米应用商店”下载“王者荣耀”的软件包就好了,就可以直接获得“王者荣耀”的可执行程序,我们就可以直接上手游戏!
4、而在Linux环境中比较常见的软件管理器就有:yum;它主要应用在Fedora,RedHat,Centos等Linux发行版!
通过命令yum list
我们可以查看,yum软件管理器里面的软件;
由于数量比较多,我们就可以筛选出软件名带有sl的软件:
这时我们配合管道进行使用:yum list | grep sl
其中:
cyrus-sasl-plain.x86_64 2.1.26-24.el7_9 @updates
1、软件包名称.cpu架构 主版本号.次版本号.源程序发行号-软件包的发行号.主机平台 软件源
2、el7_9:表示Centos7.9(操作系统的发行版本)
3、"x86_64" 后缀表示64位系统的安装包, "i686" 后缀表示32位系统安装包. 选择包时要和系统匹配.
4、最后一列,@updates表示软件的来源,有点类似与王者荣耀和天美工作室的关系;
下载软件,我们可以利用命令yum -y install xxx
比如我们现在想要下载一个小火车运行的小程序,
我们就可以直接执行命令:yum -y install sl.x86_64
我们来运行看看:
运行命令:sl
带上-y选项是忽略系统的询问,不带的话系统每次下载软件都会询问你,很是麻烦,就像这样:
这里我们只需要输入y即可完成安装!
卸载软件利用命令:yum -y remove xxx
具体化一下,我们现在想要卸载掉刚才下载好的小伙车,我们只需执行命令:yum -y remove sl.x86_64
:
同样的出现complete字样表示卸载完毕!
当然以上操作需要以root身份进行或者对命令带上sudo!!!
首先我们先明白一个问题,软件管理器下载软件是从本地下载的,还是从服务器上下载的?
答案肯定是从服务器上下载的;
不管是在电脑还是手机上都会存在一个软件——应用商店,我们下载软件可以从这里面下载,只需要输入软件名,然后点击下载即可;那么这个应用商店又是怎么下载我们需要的软件的呢?首先软件管理器中的软件是不在本地的,是在服务器上的,软件管理器就相当于中介,软件管理器在接受我们下载软件的请求过后,就去对应的服务器上寻找相应的软件包,然后给我们传回来,所以我们就能轻松下载软件了;一般来说,我们在下载应用商店的时候,都会顺带将一些配置文件下载下来,而软件管理器的“软件源”,一般都会写在这些配置文件当中,软件管理器就是根据这些软件源去寻找相关的软件,并带回来;
vi/vim的区别简单点来说,它们都是多模式编辑器,不同的是vim是vi的升级版本,它不仅兼容vi的所有指令,而且还有一些新的特性在里面。例如语法加亮,可视化操作不仅可以在终端运行,也可以运行于x window、 mac os、windows。
注意vim是个多模式编辑器!!!不是编译器!!!只能用来写代码,就像记事本一样,并不能进行调试、编译等操作!但是我们可以通过对于vim的配置,把它配置成一个与vs2019差不多的IDE;
上面说了vim是个多模式编辑器,既然是多模式,那么就一定有很多模式,我们一般只需要学习3~5种比较常用的模式就好了;
比较常见的模式有:命令模式、底行模式、插入模式、替换模式、可视化模式;
命令模式: 进行复制、粘贴、删除、剪切、撤销、替换、恢复、移动光标、切换到其他模式等操作;
底行模式: 保存信息、退出vim编辑器、查找相关字符串、大规模替换字符串、设置行号等;
替换模式: 进行文本替换操作;
插入模式: 可以进行文本的编写、修改等操作;
可视化模式: 可以进行批量操作,比如批量选择多行、多个字符等;
首先我们需要检查一下我们的Linux系统是否含有vim编辑器,如何检查?
我们直接输入命令vim
,如果出现一下界面,则表示该Linux系统是含有vim编辑器的:
如果系统出现command not found
则表示该linux系统无vim编辑器;
对于Centos系统我们可以采用命令yum -y install vim
来下载!
前提条件准备好了!我们接着进入主题:
我们利用命令vim test1.c
打开test1.c文件,如果test1.c文件不存在的话,vim会自动创建一个文件,并打开;
一般情况下,我们利用vim打开文件首先进入的就是vim的命令模式,这也是切换到其他模式的基础,我们利用vim打开的界面是这样的:
我们会发现这时,无论按什么键都没用(除了误触到其他按键,进入其他模式),显示器也没有任何反应!
如果我们这时候输入[i]
就会进入插入模式!(注意在命令模式下输入的命令是不会回显的,也就是说我们看不见自己输入的命令,但是他已经输入进去了!)这时候我们就可以进行输入了,可以进行编写代码了;
当我们再按一下Esc过后,我们就能退出插入模式,回退到命令模式;
这时我们再输入命令[:]
,我们就能进入底行模式:
我们再按一次Esc,我们就可以退出底行模式,回退到命令模式;
这时我们在输入[R]
可以进入替换模式
我们再按一次Esc,我们就可以退出替换模式,回退到命令模式;
我们这时在输入命令[v/V]
都可以进入可视化模式:
当然也可以以[v]
进入可视化模式,不过以[V]
进入有点区别!这点我们下文在讲!
总的来说这几个常用模式之间的关系如下图所示:
注意事项:
1、当我们无法确定自己处于那个模式下时,我们直接无脑Esc,就可以直接退回到命令模式!
2、当我们想要切换到其他模式时,我们得首先切回命令模式,再切换到其他模式去!
3、在vim编辑器中,我们尽量不要用鼠标去翻页;因为在实际的服务器上是没有鼠标的!
文本操作:
yy:复制光标所在行所有内容;
nyy:复制包括光标所在行的n行内容;n表示数字
p:在光标所在行的下一行开始粘贴复制、剪切的内容;
np:在光标所在行的下一行开始粘贴复制、剪切的内容,粘贴n次;
dd:删除光标所在行;
ndd:删除光标所在行以内的n行;
D:删除光标所在行,同时改行会被用空格替换;
这里我们这里组要注意一下dd命令的操作,当我们利用dd命令删除过后再输入命令p的话就会完成剪切的操作,不输入p的话就是单纯的删除操作;
x:删除光标所在位置的字符;
nx:删除以光标位置开始的n个字符;
X:删除光标位置之前的一个字符;
nX:删除光标位置之前的n个字符;
r:替换光标所在位置的字符;在执行该命令后,我们需要输入需要替换的字符;
nr:替换光标开始的位置的n个字符;
~:将光标所在位置的字母进行大小写转换(大写->小写;小写->大小);
u:撤销操作;
Ctrl+R:恢复操作;
光标移动:
G:将光标移动到全文最后一个字符的位置;
gg:将光标移动到全文开头;
nG:将光标定位到底n行开头位置;ngg也可;
$:将光标定位到本行开头位置;
^:将光标定位到本行末尾位置;
w:光标以单词为单位开始从左往右移动,字母为一组单词、非字母为一组单词、空格直接跳过!,每次都移动到单词开头位置;
b:光标以单词为单位开始从右往左移动,字母为一组单词、非字母为一组单词、空格直接跳过!,每次都移动到单词开头位置;
nw:以光标所在单词为基准向后跳n个单词;
nb:以光标所在单词为基准向前跳n个单词;
e:光标以单词为单位开始从左往右移动,字母为一组单词、非字母为一组单词、空格直接跳过!,每次都移动到单词尾部位置;
h、j、k、l:相当于方向键,控制着光标的上下左右移动;(以字符为单位)
h:想左;j:向下;k:向上;l:向右;
同时h、j、k、l也是可以配合n使用:
比如nh:以光标为基准,将光标在改行向左移动n个字符;nj、nk、nl同理;
Ctrl+f:向前翻一页;
Ctrl+b:向后翻一页;
Ctrl+u:向后翻半页;
Ctrl+d:向前翻半页;
首先在命令模式下输入:
进入底行模式;
set nu
: 设置行号;
set nonu
:取消行号;
w
:保存当前内容;
q
:退出当前文件,当然如果文件内容被修改了,但是没保存,是无法退出的;
wq
:保存文件内容并退出;
w
!:强制保存!用于一些难以修改的文件;
q!
:强制退出!不保存也能退出,不会保存文件内容;
%s/aaa/xxx/g
:将文本中的所有aaa字符串全部替换成xxx字符串;
! command
:不退出vim也可以执行对应的指令;比如在底行模式下:!ls,与在命令行解释器输入指令ls的效果是一样的;
vs filename
:利用vim再打开一个文件,此时原来打开的文件不会被关闭,新打开的文件与原文件会被分屏展示,此时光标在那个文件表示我们正在操作哪一个文件!当然如果我们想去操作另一个文件我们可以按下:Ctrl+ww来在不同文件之间来回切换文件,当我们想要退出vim时依次将我们所打开的文件关闭即可;
/key
: 从光标位置向后搜索xxx,搜索到后定位到行
?key
: 从光标位置向前搜索xxx,搜索到后定位到行
num
:光标定位到num行开头;
首先在命令模式下输入 [i]
进入插入模式;
当然进入插入模式的方式有很多,我们列举其中几个比较常见的,并分析其区别:
[i]
:进入插入模式,但是光标所在位置不变;
[a]
:进入插入模式,但是光标在原来的位置上会向后移动一位;
[o]
:进入插入模式,但是光标会在原来行的下一行重新另起一行;
处于[插入模式],就只能一直输入文字,如果发现输错了字,可以先按一下「ESC」键转到[正常模式]再利用x指令进行删除文字。当然,也可以直接删除。
首先在命令模式下输入 [R]
进入插入模式;
在进入替换模式过后,每输入一个字符光标所在位置的字符就会被替换成输入的字符,同时光标自动往后移动;当我不想替换之后按下Esc即可退出替换模式;
首先在命令模式下输入 [v]
进入插入模式;
在可视化模式下,可以对一个文本块的整体进行操作。
进入命令模式的方式有一下几种:
[v]
:进入的字符可视化模式,文本选择是以字符为单位的。
[Ctrl+v]
:进入的块可视化模式,可以选择一个矩形内的文本
在可视化模式下利用h、j、k、l移动光标可以扩大或缩小选中的范围,被选中的范围会被高亮显示!
d
:删除选中的部分文本。
D
:删除选中部分所在的行,和 d 不同之处在于,即使选中文本中有些字符所在的行没有都选中,删除时也会一并删除。
y
:将选中部分复制到剪贴板中。
p(小写)
:将剪贴板中的内容粘贴到光标之后。
P(大写)
:将剪贴板中的内容粘贴到光标之前。
u(小写)
:将选中部分中的大写字符全部改为小写字符。
U(大写)
:将选中部分中的小写字符全部改为大写字符。
I
: 进入插入模式与从命令模式输i进入插入模式效果一样;
vim可视模式下快速批量的添加注释;
比如现在我们想要将10~15行注释掉,我们就可以先以可视块模式:
然后选中10~15行开头:
然后输入[I]
进入插入模式输入//,再按Esc退出可视化模式,即可完成10~15行的批量注释:
当然如果我们想要批量取消注释,我们直接选中 [//],然后按下delete即可删除注释;
配置文件的位置:
在目录 /etc/ 下面,有个名为vimrc的文件,这是系统中公共的vim配置文件,对所有用户都有效。
而在每个用户的主目录下,都可以自己建立私有的配置文件,命名为:“.vimrc”。例如,/root目录下,
通常已经存在一个.vimrc文件,如果不存在,则创建之。
切换用户成为自己执行 su ,进入自己的主工作目录,执行 cd ~
打开自己目录下的.vimrc文件,执行 vim .vimrc
每个用户自己在自己工作目录下配置的".vimrc"文件不会影响该机器下其他用户的vim配置!
常用配置选项
设置语法高亮: syntax on
显示行号: set nu
设置缩进的空格数为4: set shiftwidth=4
对于更多的vim配置操作,我们可以上网搜索!
以上都是手动设置,当然我们也可以自动化配置;
这样我们在vim编辑器中写代码就会比较方便:
在 shell 中执行指令(想在哪个用户下让vim配置生效, 就在哪个用户下执行这个指令. 强烈 “不推荐” 直接在 root 下执行):
curl -sLf https://gitee.com/HGtz2222/VimForCpp/raw/master/install.sh -o ./install.sh && bash ./install.sh
需要按照提示输入 root 密码. 您的 root 密码不会被上传, 请放心输入.
想这样直接将链接复制到命令行解释器,然后按回车,就可以自动完成vim的配置了;
我们可以发现经过配置过后的vim编辑器,在写代码的时候已经可以出现语法提示、和函数提示了,比“裸”vim好用多了;
我们都知道C/C++编译都要经过:预处理、编译、汇编、链接这几个阶段;
这几个阶段都是编译器帮助我们完成的;
在VS2019这个集成开发环境中,这几步都是贯穿在一起的我们平时用的Ctrl+F5,就直接将这几步一起运行了,由于VS2019的封装性太好了,我们不好观察每个阶段的细节,因此我们可以学习以下在Linux系统的C/C++编译器,通过输入不同的命令来控制编译器停留在某个阶段,便于我们观察其中的细节!gcc是C语言编译器,专门用来编译C语言代码,不能用来编译C++代码;g++是C++编译器,专门用来编译C++代码,但是也是可以用来编译C语言代码,因为C++是兼容C的!
我们只需要学习其中一个,另一个也就能很快掌握!
本篇博客主要讲解gcc编译器,g++是同理的;
下面我们来回忆以下代码每个编译阶段的细节:
测试代码:
1、预处理:头文件展开、删掉注释、进行宏替换、条件编译;
如果我们想要看到预处理阶段处理过后的结果的话,我们可以利用命令:
gcc -E xxx.c -o xxx.i
-E:让gcc编译器编译到预处理阶段就停下来;
-o:将文件输出到 xxx.i文件
“.i ” 文件:已经过预处理的C原始程序
前后结果对比:
2、编译阶段:在该阶段会进行语法检查、词法检查、语义分析,当这些都没的问题了才会将预处理阶段的C语言代码转换为汇编代码!并进行符号汇总;
为了观察这一阶段的结果我们可以利用命令:
gcc -S xxx.c/xxx.i -o xxx.s
只要是在编译阶段前产生的文件都可以用来进行编译!这里我们就采用上一次预处理阶段完产生的main.i文件来进行编译,当然也可以直接利用源代码;
-S:让gcc只处理到编译阶段就停下来不在继续往后处理!
.s文件: 装载汇编语言的源代码文件;
具体细节如下:
3、汇编:生成符号表(这里可以理解为在为后面的链接做准备)、将汇编代码转换为二进制代码;
命令:gcc -c xxx.s -o xxx.o
-c: 让gcc编译器处理到汇编阶段结束就停止;
.o文件: 目标文件,也就是一个二进制文件,只不过现在的二进制文件还不能执行,因为符号表中有些符号的地址是无效的,需要在链接阶段对符号表进行重定位!(也就是让符号表中的地址合并成一个有效地址!)
小总结:
4、链接: 进行符号表的重定位,链接语言本身的库(也就是为库函数,比如printf、scanf这些符号赋予函数地址,让编译器能够通过这些函数的地址找到这些函数的实现)
命令:gcc mian.o -o main
最后经过链接过后原先的 .o二进制,就可以被执行了,因为文件里面的每个符号都对应了其真实地址!此时的符号表里面的每个符号都是有效的,编译器可以轻松找到其被定义的地方!
至此整个翻译阶段结束!只要程序一运行起来,就会根据符号表中对应的地址找到到对应的函数实现!完成相应的调用操作!
对于g++编译器上面的命令同样适用,只需要将上面命令中的gcc改为g++即可!
在日常学习中我们或许会听到什么动态库、静态库什么的!
那么对于我们“专业选手”来说库到底是个什么东西呢?
首先我们先明白,我们学习东西都是站在巨人的肩膀上!
对于学习C语言来说也是如此,我们在写程序的时候不可能每个函数都去自己实现,就比如说我们想要验证一个程序的准确性,那我们就希望大量的与程序进行交互,我们需要一个能将我们的“指令”输送给程序,然后将程序执行过后的结果反馈给我们的函数;
假设我们能够写出这样的一类函数,但是如果程序一多起来,每个程序都需这样的交互函数的话,我们都要去写一份这样的代码的话,时间长了,就会显的很麻烦、很繁琐!当然这还是能实现这些函数的“程序员”所考虑的,对于无法实现出这些函数的“程序员”来说那岂不是又增加了一项负担;为此对于这些需要在程序中被大量使用、并且具有一定实现难度的函数,开发者们在该语言发布的时候,就会将这些函数的实现实现出来,并伴随语言的发布而一并发布,当然这些函数并不只有几个,而是很多个、非常多个,如果直接发布这些函数实现的源代码的话,那么使用者在编译这些代码上就会花费大量的时间,为了提高开发效率,开发者们一般发布的都是这些函数经过编译过后所形成的 .o文件(二进制文件或者汇编结束产生的二进制文件!),而为了便于管理这些文件,开发者们将这些函数的.o文件都封装在一个特定的文件夹下面,这个文件夹就被我们称之为 “库”,我们在下载编译器,比如类似于Vs2019这一类的时候,除了会下载特定的编程语言还会将该语言自带的 “库” 也下载下来,当然这些库都是大佬们为我们写好的,我们直接用就可以,为此我们从编程学习之路才会没有那么痛苦!(图解:)
那么好,现在我们理解了“库”这个概念,那么什么又是静态库,什么又是动态库呢?
通过前文,我们可以知道,库是拿来代码链接阶段使用的,在链接阶段我们会有一个自己的 .o文件;
静态库:如果链接器将调用的库中我们需要的函数代码拷贝链接到我们自己编译产生的 .o(目标文件)里面来形成可执行文件,那么这个库就叫做静态库,对应的链接方式叫做静态链接!(静态库在)
动态库:如果链接器将调用的库中我们需要的函数的地址写入我们自己编译产生的 .o(目标文件)里面来形成可执行文件,那么这个库叫做动态库,对应的链接方式叫做动态链接!
在Linux中,以
.so
结尾的后缀,是动态库;以.a
结尾的是静态库 ;静态库:libxxx.a;动态库:libxxxx.so;前缀lib xxx:文件名 .a/.so 后缀;
在Windows中,以.dll
结尾的后缀,是动态库;以.lib
结尾的是静态库
大多数编译器都是采用的动态链接的方式!
动态库与静态库的特点:
静态库:
1、链接器采用静态链接,链接成功过后,我们的程序不依赖于任何库,完全独立运行,就算是原来的静态库被“毁了”也不影响我们的程序运行;
2、我们自己的代码体积会变大、会使我们的代码占用大量的内存、和磁盘空间;
3、如果库里面的源代码有稍微一点修改,那么我们的程序就得重新进行编译!
动态库:
1、由于是只将库里面函数的地址拷贝进我们的目标文件,那么我们的目标文件相比于静态链接所形成的可执行文件会小很多,我们可以通过测试来验证:
2、将一些程序升级变得简单,不需要重新编译,属于增量更新
3、可以实现进程之间的资源共享。(因此动态库也称为共享库)因此动态库在内存中只有一份!静态库也是如此!
在介绍这个工具之前,我们可以先想一个文体哈:就是一个比较大型的项目中,有那么多的源文件,为啥子在VS2022下一个:Ctrl+F5就能编译所有源代码,并且运行生成所有的可执行程序?
在Linux环境可就没有这样的快捷键!
我们要想对一个项目生成对应的可执行程序,就必须老老实实的利用gcc 命令一条一条的将该项目下的所有源文件、及头文件进行单独编译,然后在将这些汇编生成的.o文件链接在一起生成可执行程序!
这样麻烦不说、还有输错命令的风险!导致源文件的丢失!一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,如果仅仅使用gcc命令去一条一条执行,开发效率极低不说、还有可能导致输错命令导致数据丢失的风险!
那么在Linux环境下有没有什么办法,能够让我们像在VS2019那样的环境下直接一个Ctrl+F5就能编译我们所有的代码并且帮我们自动进行链接生成可执行文件呢?
答案是:有的;
Linux为我们提供了make命令,make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile已成为了一种在工程方面的编译方法。
具体使用方法如下:
在本项目目录下创建一个叫做makefile/Makefile的普通文件,然后在makefile文件中按照如下格式书写:
target:prerequisites
Tab键开头 command
…………
…………
…………
target:目标文件,我们想要生成的文件;
prerequisites:生成目标文件所需要的文件(或者制作目标文件所需要的“原始材料”)
command:告诉make,打算怎么利用“原始材料”生成目标文件;(任意的Shell命令)
整个流程下来,叫做一个规则!
target与prerequisites之间是依赖关系,target文件依赖于prerequisites文件,target文件和prerequisites文件可以是多个之间用空格隔开!如果prerequisites文件中至少有一个文件比target新的话,那么目标文件将会被重新制作!也就是我们可以再一次利用make命令执行这个规则!
下面我们来具体实际操作一下(模拟一下VS2019下的:Ctrl+F5)
main.c文件的具体代码:
下面我们就来直接执行一下:
我们发现直接运行成功了,比起原来的一条一条gcc 命令编译链接方便多了;
当然,我们发现Vs2019还有个一键清除功能,这个功能我们也可以模拟实现:
下面在makefile下如下编写:
想像clean这种在依赖方法中并没有实际生成clean目标文件的指令,那么也就是说在当前目录下clean目标文件不会被实际做出来,那么我们把这样一类目标文件叫做 “伪目标”,“伪目标”通常用来表示某一些列的操作的标签!
当我们输入make命令后:
1、 make命令会在当前目录下寻找名字叫makefile的普通文件,如果没有找到名叫makefile的文件,make会直接报错:
如果找到了,make会去makefile执行你指定的规则;
2、 在找到对应的规则过后,它会检查一下生成目标文件所需要的原始文件是否在当前目录下都存在,如果其中有一个文件不存在,那么make命令会在makefile文件里面从头扫描生成该文件的规则,如果没找到,则make命令报错,并直接退出;如果找到了,它会重复一下前面的检查步骤,直到生成目标文件的原式文件都在当前目录下都存在!
3、 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件
4、 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理;
图解原理:(假设所有的规则都编写正确)
这主要是make命令认为你的main程序已经是最新的了,无需重新编译生成了;
make命令是如何知道我的main程序是最新的呢?
make命令主要是通过main可执行程序的生成时间和源文件的最后修改时间进行对比,如果生成main可执行文件的原始文件的修改时间早于main可执行程序的生成时间,那么make命令就会认为当前的main可执行程序就是最新的,无需重新进行编译,当然如果生成main可执行文件的原始文件的修改时间晚于main可执行程序的生成时间,那么说明原始文件被修改过了,我的main可执行程序,已经不是最新的了,我们需要重新编译生成;
如果我们不想受这时间的约束,就想每执行一次make命令,就重新编译和运行一次该怎么办?
可以将main设置为目标文件:
我们这时可以发现多次make就不在报错了;
但是这样的方式不是我们推荐的,因为在实际的项目中我们的源代码不计其数!如果不小心输入make命令,那么就会再一次对代码进行编译,那么编译的时间花销会非常大!
如果还想了解更多makefile语法:可以看一看这篇文章:Make命令零基础教程;
前面的准备工作已经基本准备好了,我们可以用vim编辑器写代码、gcc/g++编译器编译代码、Makefile自动化构建代码;
那么现在我们来仔细的用一用这些工具!
首先我们需要明白‘\n’与’\r’的概念:
\n:严格意义上来说就是换行;
\r:严格意义上来说才是回车(也就是将光标从移动到本行开头);
我们实际中看到的\n的效果应该是:光标直接从上一行跳到下一行,不会再将光标移动到开头;
比如说:
xxxxxxxxxxxxxxxxxxxxxxx x
xxxxxxxxxxxxxxxxxxxxxxx x
这才是实际的\n该有的效果!
但是这与我们实际使用习惯有所不同,我们所想的“换行”应该是光标移动到下一行开头:
xxxxxxxxx x
x
为此语言上,对于\n进行了优化,让\n变成了我们所想的样子!
实际上应该是 \n\r 或者 \r\n;
在C/C++中输入输出并不是直接就冲键盘读取或者直接将结果输出到显示器上的,而是先输出到缓冲区,再从缓冲区内进行输入或输出!
怎么证明缓区的存在呢?
我们可以利用下面的代码在Linux环境下进行验证:
#include
#include
#include
#include
int main()
{
printf("Hello main!");
sleep(2);
return 0;
}
如果不存在缓冲区的话我们可以快速的看到“Hello main!”这句话;
如果存在缓冲区的话我们2s内看不到“Hello main!”,2s过后我们就又能看到了!
我们可以看到光标停留在开头,就是没有输出内容!
但是过了2s后就输出了内容:
这也就证明了缓冲区的存在!
那好,现在我知道了缓冲区的存在,可是为什么会有缓冲区的存在?
1、举个简单例子,就好比张三是老师,你是学生;你遇到了一个问题就去问张三,遇到一个问题就去问张三,而且其中时间间隔还很短,频率很高,这样来说,张三是不是就被你一个人给霸占了,张三也就没有机会去帮助其它同学解决问题和处理自己的工作了,这样效率很低,于是张三就对你说,你每隔30问我一次,这样是不是张三就得到了“解放”,也有时间和精力去解决其它问题;这里的缓冲区也是一样,操作系统不可能一直为你的文件输出服务,它会让你把需要输出的全“扔在”缓冲区,待缓冲区满了,在一次性送进文件里面;当然也并不一定非要缓冲区满了才推送,当缓冲区读到\n或者你利用fflush强制刷新缓冲区也是会让操作系统来帮你完成推送的,这样很大程度上解放了操作系统,让操作系统能处理更多的事情;
2、直接从文件读取数据的速度比较慢,我内存需要数据,但是我的供给跟不上啊,于是我整个程序就会停下来等你,很是浪费时间,干脆我做个缓冲区,像将数据读到缓冲区,然后我直接从缓冲区读取数据;由于直接从缓冲区读取数据,比直接从文件读取速度要快,更能从节约时间成本;
如何刷新缓冲区:
1、在输出语句里面跟\n可以刷新缓冲区;
2、在输出语句后面跟scanf/fscanf/getchar/fgetc等输入函数可以刷新缓冲区;
3、fflush函数可以强制刷新缓冲区;
4、缓冲区满了,系统自动刷新缓冲区;
4、程序结束;
注意以下代码都是在Linux环境下实现!
头文件:
#include
#include
#include
#include
#include
功能实现:
void ProBar()
{
char bar[101] = { 0 };
char ch[] = {"|/-\\"};
int i = 0;
int y = 0;
while (i <= 100)
{ printf( "\033[1;31;40m 输出红色字符 \033[0m" )//\033[显示方式;前景色;背景色m
y = rand() % 8 + 40;
int x = rand() % 8 + 30;
//printf函数可以输出颜色字体,读者感兴趣可以自行去搜索学习!
printf("[\033[1;%d;%dm%-100s\033[0m][\033[1;%d;%dm%2d\033[0m%%][%c]\r",x,40,bar,y-10 ,40,i,ch[i%4]);
fflush(stdout);
usleep(100000);
if (i < 100)
{
bar[i] = '=';
}
i++;
if (i < 100)
bar[i] = '>';
}
printf("\n");
}
double func(float x,float y)
{
float a=(x*x+y*y-1)*(x*x+y*y-1)*(x*x+y*y-1);
return a - x * x * y*y*y;
}
void Love()
{
system("clear");//清屏;
float x=0.0f;
float y=0.0f;
char key = '*';
for (y = 1.2f; y >= -1.0f; y -= 0.1f)
{
key = '*';
for (x = -1.5f; x <1.5f; x += 0.05f)
{
if (func(x, y)<=0.0)
{
int x = rand() % 8 + 30;
printf("\033[1;%d;40m%c\033[0m",x, key);
}
else
{
printf(" ");
}
}
fflush(stdout);
sleep(1);
printf("\n");
}
}
主函数:
int main()
{
ProBar();
Love();
return 0;
}
Makefile文件:
main: main.c Probar.c
gcc main.c Probar.c -o main
./main
.PHONY:clean
clean:
rm -rf main
运行结果:
git我们先来理解下 Git 工作区、暂存区和版本库概念:
工作区:就是你在电脑里能看到的目录。(也就是我们我们从远端克隆过来的文件夹)
暂存区:英文叫 stage 或 index。一般存放在 .git 目录下的 index 文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)。
版本库:工作区有一个隐藏目录 .git,这个不算工作区,而是 Git 的版本库。 clone url (远程仓库链接):将建好的远程仓库克隆到本地来;
git config --global user.name “用户名”:设置用户名;
git config --global user.email 邮箱地址:设置用户邮箱;
git config --global --list :查看当前用户配置;
git config --system --list :查看系统配置;
git init:初始化本地仓库;
git add [file1] [file2] :将指定文件的信息或版本信息添加到暂存区;
git commit :将暂存区的内容添加本地的git版本库中去(就是将当前本地仓库的更新信息添加到.git仓库里面去);
git push:将指定的文件及其信息或版本信息推送到远程仓库;
git status: 查看本地仓库的变化情况(我的理解就是展示出本地仓库与远程仓库的不同)
git log: 查看日志(就是看看你都干了什么)
git rm :删除工作区的文件(不会删除远程的,只有同步了这个操作才会删除远程的);
git mv: 移动或重命名工作区代码;
git pull: 下载远程代码并进行合并
1、程序发布方式又两种:debug版本和release版本;
debug版本是给程序员用的,debug版本含有调试信息,可以调试;
release版本是面向用户的,给用户用的,不含调试信息,会对代码进行优化!;
因此debug版本的可执行程序一般比release版本大一点:
2、Linux环境下gcc/g++编译出来的可执行程序默认是release版本;
3、若想使用gcc/g++编译器生成debug版本下的可执行程序,可以带上选项-g
;
run
(可简写r
):重新开始运行文件,中间如果有断点的话会在第一个断点处停下来!这里需要注意是第一个断点,后续如果再有断点的话,run就不会停下来了而是一口气跑完整个程序!如果整个程序没有断点的话就会跑完整个程序;
start
:开始调试程序,相当于VS2022下的F10,运行至main函数的第一条语句;
list
(可简写l
):展示代码;
l+行号
,从当前行开始展示;
break
(可简写b
):设置断点;
b+行号
:在当前文件的指定行设置断点;
b+file:函数名:xxx
:在指定file文件中的指定函数中的指定行设置断点;
next
(可简写n
):逐过程调试,不进入函数内部;
step
(可简写s
):逐语句调试,会进入函数内部;
d num
:删除指定号码的断点,该号码不是行号,而是调试器为每个断点单独设置的编号;
d breakpoints
:删除所有断点;
info b
:查看当前设置的断点信息;
finish
:首先要进入函数内部,执行完该函数,并返回函数返回值;如果中间有断点的话则会在断点处停下来;
until xxx
:在函数内部,跳到该函数的合法行号;合法行号:当前调试语句以下的、当前函数以内的行号;
c
:从一个断点跳到另一个断点;
p 变量
:打印出变量信息;只显示一次;
display 变量
:监视某个变量的信息,只要不取消就会一直显示;
undisplay 变量
:取消监视某个变量;
d display
:删除全部监视变量;
disable num
:禁用某个断点,注意是禁用不是取消!!
enable num
:启用某个断点,注意不是创建,而是原来就存在的,但是被我们禁用了;
set var 变量=xxx
:设置某个变量=xxx,看看结果会发生什么;
set print array
:以一种比较好看的方式打印数组;
set print null-stop
:设置GDB打印字符数组的时候,遇到NULL时停止;
set print pretty
:打印结构体时,每行一个成员;
info display
:查看监视变量的情况;
q
:退出gdb;
细节: gdp会记住最近一次输入的命令,所以如果我们需要进行大量重复操作时,不需要一直输入命令,直接按回车就行了;
以上便是关于Linux环境基础开发工具使用的全部内容了!
命令很多,但是只要用的多了,自然而然的就记住了!