目录
Linux编辑器-vim使用
vim的基本概念
vim的简单配置
vim的基本操作
vim进阶命令集
插入模式
从插入模式切换为命令模式
移动光标
删除文字
复制
替换
撤销上一次操作
更改
跳至指定的行
vim底行模式命令集
列出行号
跳到文件中的某一行
查找字符
保存文件
离开vim
vim简易命令集 - 最基础的必须会
命令模式的常见命令
底行模式的常见命令
Linux编译器-gcc/g++使用
背景知识
链接 - 静态库与动态库
动态库 - 动态链接
静态库 - 静态链接
动态链接 - 静态链接
总结
gcc选项
Linux项目自动化构建工具-make/Makefile
make和Makefile
背景
实例代码
伪目标
Linux第一个小程序 - 进度条
\r&&\n
\r
\n
进度条代码
Linux编辑器-vim使用
(编辑器 —— 只能写代码 —— 只能写代码)只关心如何然我们更快更高效的写出代码
vi/vim的区别简单点来说,它们都是多模式编辑器,不同的是vim是vi的升级版本,它不仅兼容vi的所有指令,而且还有一些新的特性在里面。例如语法加亮,可视化操作不仅可以在终端运行,也可以运行于x window、 mac os、windows。
vim的基本概念
普通 / 命令模式是默认的进入vim的模式。
此处只有vim的三种模式(实际上有很多模式,但是基础掌握这3种即可),分别是命令模式(command mode)、插入模式(Insert mode)和底行模式(last line mode),各模式的功能区分如下:
- 正常/普通/命令模式(Normal mode)
- 控制屏幕光标的移动,字符、字或行的删除,移动复制某区段及进入插入模式下,或者到底行模式。
- 插入模式(Insert mode)
- 只有在插入模式下,才可以做文字输入,按「ESC」键可回到命令行模式。该模式是我们后面用的最频繁的编辑模式。
- 末行模式(last line mode)
- 文件保存或退出,也可以进行文件替换,找字符串,列出行号等操作。 在命令模式下,shift+: 即可进入该模式。要查看你的所有模式:打开vim,底行模式直接输入。
vim的简单配置
vim的配置比较复杂,某些vim配置还需要使用插件,建议不要自己一个个去配置。建议在网上搜索,找一个自己喜欢的配置进行安装,想在哪个用户下让vim配置生效,就在哪个用户下执行该指令。
在配置之前,是几乎什么都没有的,而配置完成后,绝大多数,自动补全、行号显示、自动缩进等才会有。
自己调的:
- 设置语法高亮: syntax on
- 显示行号: set nu
- 设置缩进的空格数为4: set shiftwidth=4
- 等
【配置文件的位置】
-
在目录 /etc/ 下面,有个名为vimrc的文件,这是系统中公共的vim配置文件,对所有用户都有效。
-
而在每个用户的主目录下,都可以自己建立私有的配置文件,命名为:“.vimrc”。例如,/root目录下,通常已经存在一个 .vimrc 文件,如果不存在,则创建之。
-
切换用户成为自己执行 su ,进入自己的主工作目录,执行 cd ~。
-
打开自己目录下的 .vimrc 文件,执行 vim .vimrc 。
想在哪个普通用户下配置vim,就在哪个普通用户下执行该指令,不推荐直接在root下执行:(配置只针对配置的用户自身)
(注意:这个配置包只支持Centos 7)
curl -sLf https://gitee.com/HGtz2222/VimForCpp/raw/master/install.sh -o ./install.sh && bash ./install.sh
vim的基本操作
-
进入vim
在系统提示符号输入vim及文件名称后,就进入vim全屏幕编辑画面:- 不过有一点要特别注意,就是你进入vim之后,是处于[正常模式],你要切换到[插入模式]才能够输入文字。
- vim test.c(使用vim打开test.c文件)
-
[正常模式]切换至[插入模式]
- 输入a
- 输入i
- 输入o
-
[插入模式]切换至[正常模式]
- 目前处于[插入模式],就只能一直输入文字,如果发现输错了字,想用光标键往回移动,将该字删除,可以先按一下「ESC」键转到[正常模式]再删除文字。当然,也可以直接删除。
-
[正常模式]切换至[末行模式]
- 「shift + ;」, 其实就是输入「:」
- 退出vim及保存文件在[正常模式]下,按一下「:」冒号键进入底行模式,例如:
- : w (保存当前文件)
- : q! (输入q!,不存盘强制退出vim)
- : wq (输入「wq」,存盘并退出vim)
vim进阶命令集
-
插入模式
- 按「i」切换进入插入模式「insert mode」,按“i”进入插入模式后是从光标当前位置开始输入文件;
- 按「a」进入插入模式后,是从目前光标所在位置的下一个位置开始输入文字;
- 按「o」进入插入模式后,是插入新的一行,从行首开始输入文字。
-
从插入模式切换为命令模式
- 按「ESC」键。
-
移动光标
- vim可以直接用键盘上的光标来上下左右移动,但正规的vim是用小写英文字母「h」、「j」、「k」、「l」,分别控制光标左、下、上、右移一格
- 按「G」:移动到文章的最后
- 按「 $ 」:移动到光标所在行的“行尾”
- 按「^」:移动到光标所在行的“行首”
- 按「w」:光标跳到下个字的开头
- 按「e」:光标跳到下个字的字尾
- 按「b」:光标回到上个字的开头
- 按「#l」:光标移到该行的第#个位置,如:5l,56l
- 按[gg]:进入到文本开始
- 按[shift+g]:进入文本末端
- 按「ctrl」+「b」:屏幕往“后”移动一页
- 按「ctrl」+「f」:屏幕往“前”移动一页
- 按「ctrl」+「u」:屏幕往“后”移动半页
- 按「ctrl」+「d」:屏幕往“前”移动半页
-
删除文字
- 「x」:每按一次,删除光标所在位置的一个字符
- 「#x」:例如,「6x」表示删除光标所在位置的“后面(包含自己在内)”6个字符
- 「X」:大写的X,每按一次,删除光标所在位置的“前面”一个字符
- 「#X」:例如,「20X」表示删除光标所在位置的“前面”20个字符
- 「dd」:删除光标所在行
- 「#dd」:从光标所在行开始删除#行
-
复制
- 「yw」:将光标所在之处到字尾的字符复制到缓冲区中。
- 「#yw」:复制#个字到缓冲区
- 「yy」:复制光标所在行到缓冲区。
- 「#yy」:例如,「6yy」表示拷贝从光标所在的该行“往下数”6行文字。
- 「p」:将缓冲区内的字符贴到光标所在位置。注意:所有与“y”有关的复制命令都必须与“p”配合才能完成复制与粘贴功能。
-
替换
- 「r」:替换光标所在处的字符。
- 「R」:替换光标所到之处的字符,直到按下「ESC」键为止。
-
撤销上一次操作
- 「u」:如果您误执行一个命令,可以马上按下「u」,回到上一个操作。按多次“u”可以执行多次回复。
- 「ctrl + r」: 撤销的恢复
-
更改
- 「cw」:更改光标所在处的字到字尾处
- 「c#w」:例如,「c3w」表示更改3个字
-
跳至指定的行
- 「ctrl」+「g」列出光标所在行的行号。
- 「#G」:例如,「15G」,表示移动光标至文章的第15行行首
vim底行模式命令集
在使用末行模式之前,请记住先按「ESC」键确定您已经处于正常模式,再按「:」冒号即可进入末行模式。
-
列出行号
- 「set nu」: 输入「set nu」后,会在文件中的每一行前面列出行号。
-
跳到文件中的某一行
- 「#」:「#」号表示一个数字,在冒号后输入一个数字,再按回车键就会跳到该行了,如输入数字15,再回车,就会跳到文章的第15行。
-
查找字符
- 「/关键字」: 先按「/」键,再输入您想寻找的字符,如果第一次找的关键字不是您想要的,可以一直按「n」会往后寻找到您要的关键字为止。
- 「?关键字」:先按「?」键,再输入您想寻找的字符,如果第一次找的关键字不是您想要的,可以一直按「n」会往前寻找到您要的关键字为止。
- 问题:/ 和 ?查找有和区别?操作实验一下
-
保存文件
- 「w」: 在冒号输入字母「w」就可以将文件保存起来
-
离开vim
- 「q」:按「q」就是退出,如果无法离开vim,可以在「q」后跟一个「!」强制离开vim。
- 「wq」:一般建议离开时,搭配「w」一起使用,这样在退出的时候还可以保存文件。
vim简易命令集 - 最基础的必须会
命令模式的常见命令
(n代表具体的数字)
- yy:将当前光标所在的行复制,可nyy(如:3yy)
- p:将复制行进行粘贴,可np(如:2p)
- dd:将当前光标所在的行剪切(删除),可ndd(如:4dd)
- u:撤销操作
- ctrl + r:撤销u操作
- shift + g:光标定位到文件的结局
- gg:光标定位到文件的开始
- n + shift + g:光标定位到文件的任意行G
- shift + 6(^):将光标定位到当前行的最开始
- shift + 4($):将光标定位到当前行的最结尾
- w, b:以单词为单位进行光标的前后移动
- h, j, k, l:左,下,上,右
- shift + ~:大小写切换
- shift + r:进入替换模式
- r:替换光标所在的字符,可nr
- x 或 X(shift + x):删除光标所在的字符,包含之前或者之后,可nx 或 nX
底行模式的常见命令
(后跟一个「!」表强制)
- set no/nonu:打开行号,或者取消行号
- vs 文件名:分屏操作
- w:写入,w!
- q:退出,q!
- :!cmd:不退出vim执行对应的命令(执行命令行,编译,运行,查看man)等
(查看man的用法)
补充:
注释代码
- 移动光标到要注释的起始行的行首。
- 进入命令行后,Ctrl + q 进入可视块模式。
- 移动光标选中要注释的代码行,利用h, j, k, l:左,下,上,右选择需要注释的。
- Shift + i 进入插入模式,键入当前语言的注释符:双斜杠(需要注意的是这时只有首行输入了注释符号)。
- 按Esc键后,你之前选中的代码行会全部加上了注释符。
解注释代码
- 命令行模式,按ctrl + v进入可视块模式,按小写字母i。
- 利用h, j, k, l:左,下,上,右选择需要解注释的。
- 按
d
键就可全部取消注释。
Linux编译器-gcc/g++使用
有些云服务器上可能没有g++。
安装g++:sudo yum install -y gcc-c++
- 对于云服务器,默认是ceonos 7.6 or 8,默认匹配的gcc版本是4.8对于目前是完全够用的。
- 编译器gcc是一个专门用来编译链接C语言的。
- 编译器g++是一个专门用来编译链接C++语言的。
- C++兼容C所以g++也可以编译C。
背景知识
- 预处理(1.头文件展开、2.去注释、3.宏替换、4.条件编译等)
- 编译(C/C++代码翻译成汇编语言)
- 汇编(汇编代码转为可重定向二进制目标代码 - 生成机器可识别代码)
- 连接(链接 -> 多个.0,.obj -> 合并形成一个可执行.exe等)
从Linux的基础上理解:
首先用vim写一个简单的C语言文件。
#include <stdio.h>
#define NUM 100
int main()
{
printf("NUM: %d", NUM);
printf("NUM: %d", NUM);
// printf("NUM: %d", NUM);
// printf("NUM: %d", NUM);
// printf("NUM: %d", NUM);
// printf("NUM: %d", NUM);
// printf("NUM: %d", NUM);
printf("NUM: %d", NUM);
printf("NUM: %d", NUM);
return 0;
}
gcc如何完成
- 格式 gcc [选项] 要编译的文件 [选项] [目标文件]
一步达成
注意-o后必须紧跟形成的可执行程序。
补充:
不加-o,gcc默认生成a.out可执行程序。
Note:
后面讲解的都是到哪一个阶段,所以:可以源文件到汇编阶段,预处理到汇编阶段。所以操作文件可以是 .c 也可以是 .i,反正是前面的步骤即可。
完成到第一步:预处理
-E从现在开始进行程序的翻译,如果到预处理完成,就停下来!
只使用-E,不适用-o生成目标文件会打印到屏幕上。
可以使用vim编译器打开test.i。
我们可以发现,我们就写了几行代码,但是这却有
1. 头文件展开
#问:为什么突然会多这么多?
不管是我们直接写程序,还是用库,都会包含C头文件或者是C++头文件。当我们实际在做编译的时候,对应的头文件的内容,是会拷贝到我们的源文件中的,所以头文件是需要编译的,在预编译的时候就会拷贝到对应的位置。所以,头文件为了防止重复拷贝也就会使用:
- 条件编译(举例如下:根据文件名命名就可以了)
#ifndef _HEADERNAME_H
#define _HEADERNAME_H
...//(头文件内容)
#endif当头文件第一次被包含时:它会被执行,而符号_HEADERNAME_H被定义为1。如果头文件被再次包含,通过条件编译,它的内容被忽略(头文件内容)。
- #pragma once
并且也因为头文件里还包含的有其他头文件,于是也就更多了。
2. 去注释
3. 宏替换
4. 条件编译
因为Linux默认下为release。
Note:
通过上面的观察,我们可以知道预处理阶段其实还是C语言 / C++语言。
完成到第二步:编译
-S从现在开始进行程序的翻译,如果到编译完成,就停下来!
可以使用vim编译器打开test.i。
Note:
通过上面的观察,我们可以知道编译阶段变为了汇编语言。
完成到第三步:汇编
-c从现在开始进行程序的翻译,如果到汇编完成,就停下来!
可以使用vim编译器打开test.o。
Note:
通过上面的观察,我们可以知道汇编阶段变为了二进制语言。
完成到第四步:链接
也就是我们之前所写的一步达成。
链接 - 静态库与动态库
其实在生成可执行程序之后,可执行程序其实也还是依赖着C语言 / C++语言库的。
一般链接的过程,是有两种方式的:
- 动态链接 - 需要动态库
- 静态链接 - 需要静态库
#问:这个代码能不能编译过?
int main() { return 0; }
是能编译过的。
Linux下是会保存C / C++需要使用的头文件的。 云服务器在路径/usr/include下:
平时我们所写的代码,就以最简单的为例:打印hello world。我们使用printf函数打印字符串"hello world\n",我们是实现printf函数了吗?并没有,但是他有被调用了,证明一定有实现体,这就是因为C语言提供的。C语言通过头文件提供的。更直接的说,其是通过库供给我们使用的。
云服务器下的库路径:/lib64/下:
包含了Linux下的大部分动静态库,就是因为有动静态库的存在,所以我们才可以通过头文件找到方法的声明,再到库当中找到方法的实现。将我们的代码与库中的代码关联起来,才形成了一个可执行程序。
Linux:
.so(动态库)
.a(静态库)
windows:
.dll(动态库)
.lib(静态库)
#问:那么我们安装VS2019、VS2022的组件安装是在干什么?
也就是安装C / C++所要依赖的头文件与库罢了。
总结:
也就是说,当我们的源文件编译到汇编后,形成的目标可重定向二进制文件,这一步只是将我们直接所实现的代码编译完了(文本到二进制)。而编译的链接就是为了将我们代码当中未和库关联起来的部分关联起来,最后我们的程序才能执行。(编译的链接可以理解为printf之类的地址是未定义的)
动态库 - 动态链接
一个故事理解:一个同学他刚刚初中毕业,上了一所高中,他想完电脑,但是因为学校不允许有电脑,于是没有办法带着。但是一个人告诉他有一个网吧转弯过去就是。于是这个同学知道了这个网吧的地址,但是不知道是怎么样的网吧,但是他记住了,于是他立了一个计划:
- 上课 - 上数学课干什么等(程序员所自己写的函数的实现)
- 写作业 - 写数学作业等(程序员所自己写的函数的实现)
- 去网吧上网(C语言提供了一个函数,编译的时候知道存在,但是不知道实现,就如同这位同学只知道这个网吧的地址。C语言告诉程序员有这个实现,一个人告诉同学有这个网吧)
于是同学到执行这个计划的时候,就根据这个地址,跑去网吧找电脑了。也就是编译根据地址跑去找动态库。
静态库 - 静态链接
一个故事理解:这个同学上大学了,学校允许带电脑了,这个时候,他将原本高中时的电脑买了一个,于是将电脑带在了身边,这个时候,他立了一个计划:
- ……(程序员所自己写的函数的实现)
- 用自己的电脑上网(电脑已经在身边了,将网吧的电脑带在了身边,就不用根据地址找电脑了。如同程序数据直接放在了需要的位置,不用根据地址去找寻了)
于是同学到执行这个计划的时候,直接拿出了身边的电脑。也就是编译已经将数据在所需的地方了。
动态链接 - 静态链接
一个故事理解:这样的同学很多,于是跑网吧,这个时候一台电脑可以多人用(一个电脑可以多人玩),而带在身上,就一个同学一个电脑。所以比起来动态链接,静态链接更节省空间。
总结
- 动态链接:将库中我们要的方法的地址,填入我们的可执行程序中,建立关联 —— 节省空间。
- 静态链接:将库中我们要的方法的实现,真的拷贝到我们的可执行程序中 —— 占用空间。
云服务器有可能没有
C静态库安装:sudo yum install -y glibc-static
C++静态库安装:sudo yum install -y libstdc++-static
gcc选项
- -E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面
- -S 编译到汇编语言不进行汇编和链接
- -c 编译到目标代码
- -o 文件输出到 文件
- -static 此选项对生成的文件采用静态链接
- -g 生成调试信息。GNU 调试器可利用该信息。
- -shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.
- -O0 -O1 -O2 -O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
- -w 不生成任何警告信息。
- -Wall 生成所有警告信息。
Linux项目自动化构建工具-make/Makefile
make和Makefile
- make是一个命令
- Makefile是一个文件
用于:自动化的构建项目。
背景
- 会不会写makefifile,从一个侧面说明了一个人是否具备完成大型工程的能力
- 一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefifile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作
- makefifile带来的好处就是 —— “自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
- make是一个命令工具,是一个解释makefifile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefifile都成为了一种在工程方面的编译方法。
- make是一条命令,makefifile是一个文件,两个搭配使用,完成项目自动化构建。
实例代码
编写Makefile:
- 依赖关系
- 依赖方法
利用依赖关系与依赖方法,达到自动化的构建项目。
在具有一定的依赖关系:二者具有一定联系、关系的事物。才能实现一定的依赖方法:因为这个一定联系、关系所才能达到一定事、任务。
依赖关系、依赖方法
伪目标:假的目标,为的就是实现下面的依赖方法。
Note:
使用make默认是执行第一个依赖关系与依赖方法。
如果我们将Makefile改成下列这样的话。
make只会自顶向下,扫描第一个目标文件。进行执行。
伪目标
总是被执行的!
如果将test也变为伪目标。
这个时候其也可以变为总是被执行的!
总是被执行:
总是会根据依赖关系,执行依赖方法!习惯:clean设置.PHONY
#问:Makefile是如何得知,我们的可执行程序是最新的?
根据文件的最近修改时间的来的!
start命令查看时间
- Acess:文件最近被访问的时间(如:修改)。
- Modify:文件内容的改变时间。
- Change:属性的改变时间(如:该权限)。
Note:
Acess:文件最近被访问的时间,不一定打开文件然后关闭就改变。这是因为Linux操作系统对时间进行的调整,因为平时读取文件是一个高频操作,也就意味着Acess是被高频修改的。而文件处于磁盘,那么每一次修改Acess,都需修改文件处于磁盘对应的Acess数据。所以大量的IO就会耗费资源。所以,Linux进行了修改,是累积到一定的次数才会修改文件处于磁盘对应的Acess数据。
而Makefile得知我们的可执行程序是最新的,是通过可执行程序比源文件新,那么只有源文件动内容了,才会导致源文件比可执行程序新,才会make成功。
Makefile的多文件使用:
test.h
#pragma once #include <stdio.h> extern void show();
main.c
#include "test.h" int main() { show(); return 0; }
test.c
#include "test.h" void show() { printf("hello test!\n"); }
第一个目标文件就是利用链接实现,而其所依赖的方法中有两个目标文件,于是便会又去执行,两个目标文件各自的依赖方法。
Linux第一个小程序 - 进度条
第一个程序
第二个程序
通过执行会发现,第一个程序是先打印出hello world,后sleep(3)。第二个程序是先后sleep(3),打印出hello world。
#问:难道是第二个程序printf比sleep后执行?
不是,也不可能,一个程序的执行是按照一个代码一个代码来的,所以printf一定比sleep先执行。并且是早就执行完了!
只不过是信息("hello world")没有被立马显示出来!C语言是会给我们提供输出缓冲区(就是C语言给我们提供的一段内存空间)的,根据特定的刷新策略,来进行刷新!
显示器设备,一般的刷新策略是行刷新,是碰到"\n",就立马将"\n"之间的所有的字符全部的显示出来。
我们可以使用fflush将缓冲区的数据立马进行刷新。
在语言级的学习中,就提到过C语言其实是默认打开三个文件的。标准输入、标准输出、标准错误。而stdout就是标准输出,就是显示屏,意思就是将缓冲区数据立马刷新到屏幕上。
\r&&\n
\r
\n
所以,我们平时生活中所想的换行,其实在计算机本质是:换行 + 回车。所以这也是键盘走式Enter原因:
我们所写的"\n"之所以会变为,换行 + 回车的情况,是因为C语言中的,编译器编译的时候默认就将"\n"变为了回到开头 + 换行。
进度条代码
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define NUM 102
int main()
{
char bar[NUM];
memset(bar, 0 ,sizeof(bar));
const char *lable="|/-\\"; //4符号
int cnt = 0;
while(cnt <= 100)
{
printf("[%-100s][%d%%] %c\r", bar, cnt, lable[cnt%4]);
bar[cnt++] = '#';
fflush(stdout);
usleep(30000);
}
printf("\n");
return 0;
}