vim编辑器
vim的概念
vim就是我们写代码来用的可以理解为一个记事本,本质上就是一个多模式的文本编辑器
vim有多种模式本文主要介绍命令模式,插入模式以及末行模式
1. 命令模式(Normal mode)
控制屏幕光标的移动,字符、字或⾏的删除,移动复制某区段及进⼊插入模式下,或者到 末行模式
2. 插入模式(Insert mode)
只有在插入模式下才能进行文字输入,该模式是我们使用最频繁的编辑模式。
3. 末行模式(last line mode)
⽂件保存或退出,也可以进⾏⽂件替换,找字符串,列出⾏号等操作。
vim模式之间的切换
下图演示了三种模式之间的切换
插入模式和末行模式之间的转换都可以先回到命令模式在进行转换。
vim命令模式下的命令集合
- 命令模式下和插入模式之间的切换
- 按「i」切换进⼊插⼊模式「insert mode」,按“i”进⼊插⼊模式后是从光标当前位置开始输⼊⽂件;
- 按「a」进⼊插⼊模式后,是从⽬前光标所在位置的下⼀个位置开始输⼊⽂字;
- 按「o」进⼊插⼊模式后,是插⼊新的⼀⾏,从⾏⾸开始输⼊⽂字。
- 插入模式下按[Esc]进入命令模式
- 移动光标的操作
按键 | 作用 |
---|---|
h | 控制光标向左移动一个字符 |
j | 控制光标向下移动一个字符 |
k | 控制光标向上移动一个字符 |
l | 控制光标向右移动一个字符 |
shift+g | 快速将光标移动到最后 |
n+shift+g | 将光标移动到第n行的开头 |
gg | 快速是光标回到开头 |
$ | 将光标移动到本行的末尾 |
^ | 将光标移动到本行的开头 |
w(n+w) | 以单词为单位向后移动n个单词 |
b(n+b) | 以单词为单位向前移动n个单词 |
nl | 光标移到该⾏的第#个位置,如:5l,56l |
Ctrl+b | 屏幕往“后”移动⼀⻚ |
Ctrl+f | 屏幕往“前”移动⼀⻚ |
Ctrl+u | 屏幕往“后”移动半⻚ |
Ctrl+f | 屏幕往“前”移动半⻚ |
- 删除字符操作
按键 | 作用 |
---|---|
x(n+x) | 例如,「6x」表⽰删除光标所在位置的“后⾯(包含⾃⼰在内)”6个字符 |
shift+x相等于大写X(nX/n+shift+x) | 例如,「20X」表⽰删除光标所在位置的“前⾯”20个字符 |
dd | 删除光标所在的一行内容 |
n+dd | 删除光标所在行开始的n行 |
- 复制粘贴
按建 | 作用 |
---|---|
yy(n+yy) | 从当前光标开始计算的n行 |
yw(n+w) | 复制n个字符到缓冲区 |
p | 将复制内容从光标所在行的下一行开始粘贴 |
- 替换
按键 | 作用 |
---|---|
r | 替换光标所在处的字符。 |
R | 替换光标所到之处的字符,直到按下「ESC」键为⽌。 |
- 撤销的操作
- u:如果您误执⾏⼀个命令,可以⻢上按下u,回到上⼀个操作。按多次“u”可以执⾏多次回复。
- ctrl + r: 撤销的恢复
vim末行模式下的操作
在使⽤末⾏模式之前,请记住先按「ESC」键确定您已经处于正常模式,再按「:」冒号即可进⼊末⾏模式。
按键 | 作用 |
---|---|
set nu | 会在⽂件中的每⼀⾏前⾯列出⾏号。 |
n | n代表一个数字在冒号后输⼊⼀个数字,再按回⻋键就会跳到该⾏了 |
/+关键字 | 先按/键,再输⼊您想寻找的字符,如果第⼀次找的关键字不是您想要的,可以⼀直按n会往后寻找到您要的关键字为⽌。 |
?+关键字 | 先按?键,再输⼊您想寻找的字符,如果第⼀次找的关键字不是您想要的,可以⼀直按n会往前寻找到您要的关键字为⽌。 |
w | 保存文件 |
q | 离开vim |
wq | 保存并退出vim |
nonu | 取消行号 |
%s/dst/src/ | 将src替换为dst |
vs+文件 | 进行分屏操作 |
末行模式下输入!+命令可以直接运行
vim使用技巧
!v 代表执行v开头的最近的一条命令
vim 文件 +n打开文件光标在n行
vim的配置
配置文件的位置
- 在⽬录 /etc/ 下⾯,有个名为vimrc的⽂件,这是系统中公共的vim配置⽂件,对所有⽤⼾都有效。
- ⽽在每个⽤⼾的主⽬录下,都可以⾃⼰建⽴私有的配置⽂件,命名为:“.vimrc”。例如,/root⽬录下,通常已经存在⼀个.vimrc⽂件,如果不存在,则创建之。
- 切换⽤⼾成为⾃⼰执⾏ su ,进⼊⾃⼰的主⼯作⽬录,执⾏ cd ~
- 打开⾃⼰⽬录下的.vimrc⽂件,执⾏ vim .vimrc,如下操作:
- 设置语法⾼亮: syntax on
- 显⽰⾏号: set nu
- 设置缩进的空格数为4: set shiftwidth=4
上述的配置只可以进行简单的vim配置对于自动化补齐还没有下面我们推荐插件配置
centos用户可以通过该链接进行配置点解这里跳转
在 shell 中执行指令(想在哪个用户下让vim配置生效, 就在哪个用户下执行这个指令. 强烈 “不推荐” 直接在 root 下执行):
gcc/g++编译器
编译器自举
点击编译器自举
下面来看以下代码来看下程序的翻译过程
#include<stdio.h>
#define N 100
int main()
{
printf("hello world\n");
#ifdef N
printf("hello N\n");
#else
printf("hello no N\n");
#endif
return 0;
}
预处理
gcc -E test.c -o test.i
gcc -E -o test.i test.c
这里推荐第一种写法下面看test.i文件
可以看到代码被展开。因此预处理的作用就是宏定义,⽂件包含,条件编译,去注释等。
编译(生成汇编)
gcc -S test.i -o test.s
gcc -S -o test.s test.i
下面我们来看一下test.s里面的内容
到编译这一步形成的文件已经不是c语言了而是汇编语言在这个阶段中,gcc ⾸先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的⼯作,在检查⽆误后,gcc 把代码翻译成汇编语⾔。
- 编译过程为 扫描程序–>语法分析–>语义分析–>源代码优化–>代码生成器–>目标代码优化;
- 扫描程序进行词法分析,从左向右,从上往下扫描源程序字符,识别出各个单词,确定单词类型语法分析是根据语法规则,将输入的语句构建出分析树,或者语法树,也就是分析树parse tree或者语法树syntax tree
- 语义分析是根据上下文分析函数返回值类型是否对应这种语义检测,可以理解语法分析就是描述一个句子主宾谓是否符合规则,而语义用于检测句子的意思是否是正确的
- 目标代码生成指的是,把中间代码变换成为特定机器上的低级语言代码。
汇编(⽣成机器可识别代码)
gcc -c test.s -o test.o
gcc -c -o test.o test.s
gcc -c test.s(自动生成同名.o)
此时文件已经是二进制了但是是不可执行的就算我们给该文件加上可执行权限也是无法执行的因为我们只是把我们自己写的代码翻译为了二进制,但是我们其中调用的函数在对应的库中我们并没有自己实现因此还需要进行链接。
链接
gcc test.o -o test.exe
这样程序的翻译到此结束形成了一个可执行文件
动静态链接和动静态库
这里简单说一下后续会进行详细讲解
- 动态链接就是调用库里面的函数时去库里面执行
- 静态链接就是把库里面的函数实现拷贝到我们的程序
动静态库的概念
- 静态库是指编译链接时,把库⽂件的代码全部加⼊到可执⾏⽂件中,因此⽣成的⽂件⽐较⼤,但在运⾏时也就不再需要库⽂件了。其后缀名⼀般为“.a”
- 动态库与之相反,在编译链接时并没有把库⽂件的代码加⼊到可执⾏⽂件中,⽽是在程序执⾏时由运⾏时链接⽂件加载库,这样可以节省系统的开销。动态库⼀般后缀名为“.so”,如前⾯所述的libc.so.6 就是动态库。gcc 在编译时默认使⽤动态库。完成了链接之后,gcc 就可以⽣成可执行文件如下所⽰。
查看可持续程序依赖的的动态库用ldd
虽然gcc和g++默认采用的是动态链接,但如果我们需要使用静态链接,带上-static选项即可。
gcc test.c -o test.exe -static
注意如果不成功可能是没有安装静态库执行以下命令
yum install glibc-static libstdc++-static -y
可以看到静态链接的可执行程序的空间大
动静态库对比:
- 动态库形成的可执行程序体积一定很小
- 可执行程序对静态库的依赖度小,动态库不能缺失
- 程序运行,需要加载到内存,静态链接的,会在内存中出现大量的重复代码。
- 动态链接,比较节省内存和磁盘资源!
Linux项目自动化构建工具 - make/Makefile
make和Makefile是什么
- 会不会写makefile,从⼀个侧⾯说明了⼀个⼈是否具备完成⼤型⼯程的能⼒
- ⼀个⼯程中的源⽂件不计数,其按类型、功能、模块分别放在若⼲个⽬录中,makefile定义了⼀系列的规则来指定,哪些⽂件需要先编译,哪些⽂件需要后编译,哪些⽂件需要重新编译,甚⾄于进⾏更复杂的功能操作
- makefile带来的好处就是⸺“⾃动化编译”,⼀旦写好,只需要⼀个make命令,整个⼯程完全⾃动编译,极⼤的提⾼了软件开发的效率。
- make是⼀个命令⼯具,是⼀个解释makefile中指令的命令⼯具,⼀般来说,⼤多数的IDE都有这个命令,⽐如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可⻅,makefile都成为了⼀种在⼯程⽅⾯的编译⽅法。
- make是⼀条命令,makefile是⼀个⽂件,两个搭配使⽤,完成项⽬⾃动化构建。
Makefile基本语法
我们在大型项目可能有无数个原文件需要我们进行编译形成.o在把这些.o链接在一起形成一个可执行程序如果我们一个一个去敲就很麻烦,而我们的make、Makefile就是来解决该问题的下面展示了Makefile的基本用法。
1.首先在我们的工作目录下穿件Makefile文件接着在同一目录下创建我们的源文件如下
2. 编写Makefile文件
3. 执行make指令完成编译
4. make clean清理
对于上面的用法只要会写就可以因为就只是语法
.OPHONY关键字
makefile中使用 .PHONY 来声明伪对象;
makefile中的伪对象表示对象名称并不代表真正的文件名,与实际存在的同名文件没有相互关系,因此伪对象不管同名目标文件是否存在都会执行对应的生成指令。
伪对象的作用有两个:
- 使目标对象无论如何都要重新生成。
- 并不生成目标文件,而是为了执行一些指令。
stat指令文件的三个时间
那问题就来了那为什么我们在编译源文件时不加.PHONT关键字呢?
因为我们期望我们写的源文件内容如果不发生改变的话就不需要进行重新编译那Makefile怎么知道文件内容是否需要重新编译呢?
这就要说的一个文件的三个时间时间了如下图所示
有了上述对三个时间的理解我们在进行编译时就是比较可执行文件的Modify与我们源文件的Modify时间。如果源文件的Modify时间比可执行文件的modify时间新就重新编译该源文件。
linux小程序-进度条
我们在写该程序之前我们要理解一个概念回车和换行以及缓存区之间的关系
我们的printf会把我们打印的字符放入到缓冲区中遇到换行符刷新。如果我们不加换行符怎么刷新我们的缓冲区呢就可以用我们的fflush函数刷新缓冲区下面我们来写一个从11到0的倒计时
效果显示
有了上面代码的理解我们的进度条就很好编写了
代码如下
//main.c
#include"progress.h"
typedef void (*call)(double total,double current);
void download(call c,double total)
{
srand((unsigned int)time(NULL));
double cur=0;
while(cur<=total)
{
c(total,cur);
int speed =rand()%11;
usleep(50000);
cur+=speed;
}
c(total,total);
printf("\n%s\n","下载完成!!!");
}
int main()
{
//shouwprogressbar();
download(filueshprocessbar,1024.0);
// download(filueshprocessbar,2133);
return 0;
}
//progress.h
#pragma once
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
//vesion1
void shouwprogressbar();
void filueshprocessbar(double total,double cur);
//progress.c
#include"progress.h"
#include<unistd.h>
#define N 101
#define flag '#'
void shouwprogressbar()
{
char buffer[N]={0};
int index =0;
char * str="|/-\\";
int len =strlen(str);
while(index<101)
{
printf("\033[0;32m[%-100s]\033[1;31m[%d%%]\033[0;36m[%c]\033[0m\r",buffer,index,str[index%len]);
fflush(stdout);
buffer[index++]=flag;
usleep(50000);
}
printf("\n");
}
void filueshprocessbar(double total,double cur)
{
char buffer[N]={0};
static int index =0;//用来维持cur不改变但是仍在加载的情况
char * str="|/-\\";
int len =strlen(str);
double rate =cur/total;
int num =(int)(rate*100);
for(int i=0;i<num;i++)
{
buffer[i] =flag;
}
printf("\033[0;32m[%-100s]\033[1;31m[%.1lf%%]\033[0;36m[%c]\033[0m\r",buffer,rate*100,str[index%len]);
fflush(stdout);
index++;
}
效果展示:
gdb/cgdb调试器使用
准备
程序发布方式:
1、debug版本:程序本身会被加入更多的调试信息,以便于进行调试。
2、release版本:不会添加任何调试信息,是不可调试的。
在Linux当中gcc/g++默认生成的可执行程序是release版本的,是不可被调试的。如果想生成debug版本,就需要在使用gcc/g++生成可执行程序时加上-g选项。指令如下
gcc test.c -o test.exe -g
调试常用指令
【进入gdb】
指令: gdb 文件名
- 调试
- 「run/r」:运行代码(启动调试)。
- 「next/n」:逐过程调试。
- 「step/s」:逐语句调试。
- 「until 行号」:跳转至指定行。
- 「finish」:执行完当前正在调用的函数后停下来(不能是主函数)。
- 「continue/c」:运行到下一个断点处。
- 「set var 变量=x」:修改变量的值为x。
- 显示
- 「list/l n」:显示从第n行开始的源代码,每次显示10行,若n未给出则默认从上次的位置往下显示.。
- 「list/l 函数名」:显示该函数的源代码。
- 「print/p 变量」:打印变量的值。
- 「print/p &变量」:打印变量的地址。
- 「print/p 表达式」:打印表达式的值,通过表达式可以修改变量的值。
- 「display 变量」:将变量加入常显示(每次停下来都显示它的值)。
- 「display &变量」:将变量的地址加入常显示。
- 「undisplay 编号」:取消指定编号变量的常显示。
- 「bt」:查看各级函数调用及参数。
- 「info/i locals」:查看当前栈帧当中局部变量的值。
- 断点
- 「break/b n」:在第n行设置断点。
- 「break/b 函数名」:在某函数体内第一行设置断点。
- 「info breakpoint/b」:查看已打断点信息。
- 「delete/d 编号」:删除指定编号的断点。
- 「disable 编号」:禁用指定编号的断点。
- 「enable 编号」:启用指定编号的断点。
- 退出gdb
- 「quit/q」:退出gdb。
- 调试技巧:
1. watch
执⾏时监视⼀个表达式(如变量)的值。如果监视的表达式在程序运⾏期间的值发⽣变化,GDB 会暂停程序的执⾏,并通知使⽤者
2. set var确定问题原因
3. 条件断点 - 添加条件断点:
b 9 if i == 30 # 9是⾏号,表⽰新增断点的位置
- 在已存在的断点新增条件
condition 2 i==30 #给2号断点,新增条件i==30