vim
1、VS是集成开发环境,所有功能集于一体。
在linux中拆分为1、编辑器 vim;2、编译器 gcc; 3、调试器 gdb
2、vi 就是简单版的vim,vim就是增强版的vi
配置vim
基本配置
本质是将我们的配置项放入.vimrc文件中
[yyq@localhost 2022_09_26_lesson5]$ cd ~ #回到用户的家目录
[yyq@localhost ~]$ touch .vimrc
[yyq@localhost ~]$ vim .vimrc
[yyq@localhost ~]$ cat .vimrc
set nu
-----------以上是最简单的,下面记录课堂给的(针对云服务器的配置)------------
[yyq@localhost ~]$ curl -sLf https://gitee.com/HGtz2222/VimForCpp/raw/master/install.sh -o ./install.sh && bash ./install.sh
[yyq@localhost ~]$ source ~/.bashrc
插件配置
vim的多种模式
修改vim主题配色 :colorscheme+空格+Tab
任何模式切换,都可以先esc切换到命令模式,再去别的模式。
1、命令模式(默认模式)esc
当一个文件所处的命令模式时,输入的任何内容都被当作命令了。
控制屏幕光标的移动,字符、字或行的删除,移动复制某区段及进入Insert mode下,或者到 last line mode。
shift+4 #定位行尾(行左)
shift+6 #定位行开头(行右)
# 上述称为 锚点
##################
shift+g #定位文件末尾
g+g #定位文件开头
数字shift+g #定位到第 数字 行
b #光标按单词进行前移
w #光标按单词进行后移
h #光标一个一个地左移
j #光标一个一个地下移
k #光标一个一个地上移
l #光标一个一个地右移
##################
yy #复制光标所在行
p #粘贴
yy p #复制1次
number p #粘贴number次
number yy #复制从当前行开始的number行
number yy number p #批量拷贝
u #撤销操作
ctrl+r #取消u操作
dd #剪切光标所在行
number dd #剪切从当前行开始的number行
x #删除光标所在的位置之后一个字符
number x #批量化删除字符
shift+x #删除光标所在的位置之前一个字符
number shift+x #批量化删除字符
shift+r #替换光标所在的一个字符
number shift+r #批量化替换
cw #更改光标所在处的字到字尾处
cnumberw #如c3w表示更改3个词
###########################
shift+~ #大小写转换
vs xxx.c #vim同时打开2个文件窗口
ctrl+w+w #在两个窗口间切换
2、插入模式a/i/o
只有在Insert mode下,才可以做文字输入,按「ESC」键可回到命令行模式。
a #光标后移
i #光标位置不变
o #光标新起一行
s #光标删除一个字符
3、底行模式:
文件保存或退出,也可以进行文件替换,找字符串,列出行号等操作。
在命令模式下,shift+: 即可进入该模式。要查看你的所有模式:打开vim,底行模式直接输入
:set nu #设置行号
:wq #保存并退出
:w! #强制保存
########################
:vs filename #文件夹下有这个文件,就会分屏打开,没有就会新建再打开
########################
:!命令 #执行linux命令
:!gcc file.name #不关闭窗口直接编译
:!man 3 function #直接查function的描述
:%s/printf/cout/g #s是替换,把printf替换成cout,g是global的意思
gcc/g++
安装gcc和g++
sudo yum install -y gcc-c++
程序编译过程
1、预处理(进行宏替换) -E
头文件展开、去注释、宏替换、条件编译
处理之后还是c语言
gcc –E hello.c –o hello.i #预处理后,将临时内容写入hello.i文件
2、编译(生成汇编) -S
把c语言变成汇编语言
gcc –S hello.i –o hello.s
gcc –S hello.c –o hello.s
3、汇编(生成机器可识别代码)-c
.o称为目标可重定向文件,是二进制文件,不可执行。因为printf是库函数,我们需要把第三方库链接进来才能变成可执行程序。
gcc –c hello.s –o hello.o #把汇报语言变成目标可重定向文件
gcc –c hello.c –o hello.o
od test.o #以8进制的形式查看二进制文件
od以8进制的形式查看二进制文件
4、连接(生成可执行文件或库文件)
gcc会根据文件类型(后缀),引入我们在代码中所使用的第三方库,比如c库。这个链接是由编译器和文件类型共同决定的。比如写test.cpp就会链接到cpp的库。
gcc hello.o –o hello #把目标可重定向文件变成可执行文件
gcc hello.c –o hello
ldd hello #查看链接的库
[yyq@localhost 2022_09_26_lesson5]$ ldd hello
linux-vdso.so.1 => (0x00007ffc5cbfe000)
libc.so.6 => /lib64/libc.so.6 (0x00007f2bbae40000) #这里是关键,libc,c是c库。libc.so.6是动态链接库。libc.a是静态库。库真正的名字去掉lib,去掉.xxxx,libc.so.6就是c
/lib64/ld-linux-x86-64.so.2 (0x00007f2bbb20e000)
动态库.so 静态库.a
在linux下库的命名:动态库:libxxx.so,静态库:libxxxx.a,去掉前缀,去掉.a/.c后缀,剩下的就是库的名称。
Linux下默认形成可执行程序,默认使用的是动态库,如libc-2.17.so。
[yyq@VM-8-13-centos ~]$ ls /lib64/libc.so.6 -l
lrwxrwxrwx 1 root root 12 Jul 25 16:58 /lib64/libc.so.6 -> libc-2.17.so
[yyq@VM-8-13-centos ~]$ ls /lib64/libc-2.17.so -l
-rwxr-xr-x 1 root root 2156592 May 19 2022 /lib64/libc-2.17.so
libc.so.6是动态链接库,属于弱连接。libc.a是静态库,属于强连接。
[yyq@VM-8-13-centos ~]$ gcc test.c -o test.exe
[yyq@VM-8-13-centos 2022_11_27_BasicTool]$ file test.exe
test.exe: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=581edf32039ef04f8b42db07c90975d4c1ae1517, not stripped
//dynamically linked (uses shared libs)可以看出是动态链接的
[yyq@VM-8-13-centos ~]$ gcc test.c -o test.s.exe -static //不知道为啥我的虚拟机上运行不了这个代码,报错
#报错如下
/usr/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status
解决方法(网上找的能用,在lib64下面也会有libc.a这个库,实际上下载了是在/usr/lib64/libc.a
下,如果lib64找不到的话可以cp libc.a /usr/lib64/libc.a
)
#查看libc.a是否已经安装
sudo find / -name 'libc.a'
#centos系列安装
sudo yum install -y glibc-static
同理c++的静态库安装命令为
sudo yum install -y libstdc++-static
安装完成以后,使用file和ldd
file查看文件动/静态链接 ldd查看可执行文件的动态链接库
[yyq@VM-8-13-centos 2022_11_27_BasicTool]$ gcc test.c -o test_static -static
[yyq@VM-8-13-centos 2022_11_27_BasicTool]$ file test_static
test_static: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=a7cd36e27f10320e5bf6844e6e22a935212330bd, not stripped
//statically linked可以看出是静态链接的
[yyq@VM-8-13-centos 2022_11_27_BasicTool]$ ldd test.exe
linux-vdso.so.1 => (0x00007ffd097f1000)
libc.so.6 => /lib64/libc.so.6 (0x00007fcefccdc000)
/lib64/ld-linux-x86-64.so.2 (0x00007fcefd0aa000)
//.c编译生成的可执行文件 动态链接库有3个
[yyq@VM-8-13-centos 2022_11_27_BasicTool]$ ldd test_static
not a dynamic executable
//.c编译生成的可执行文件 使用静态库编译的可执行文件就无法查看动态链接库
[yyq@VM-8-13-centos 2022_11_27_BasicTool]$ ldd mytest
linux-vdso.so.1 => (0x00007ffdf9ffb000)
libstdc++.so.6 => /home/yyq/.VimForCpp/vim/bundle/YCM.so/el7.x86_64/libstdc++.so.6 (0x00007f22563cb000)
libm.so.6 => /lib64/libm.so.6 (0x00007f22560c9000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f2255eb3000)
libc.so.6 => /lib64/libc.so.6 (0x00007f2255ae5000)
/lib64/ld-linux-x86-64.so.2 (0x00007f225674c000)
//.cpp编译生成的可执行文件 有6个动态链接库
一般而言,系统会自动携带动态库,静态库需要自己安装。为什么呢?因为一般而言,系统运行要用的是动态链接库。
系统本身为了支持我们编程,给我们提供了标准库的.h(告诉我们怎么用),标准的动静态库.so/.a(告诉我们,它有方法实现)。
我们的程序+库的代码==可执行程序。
make/makefile
make是一条命令,makefile是一个文件
多个文件怎么编到一起呢?
1、将多文件统一先编译成.o文件,再将*o统一链接生成.exe文件
gcc -c test1.c -o test1.o
gcc -c test2.c -o test2.o
gcc -c test3.c -o test3.o
#然后
gcc test1.o test2.o test3.o -o test.exe
#实例
[yyq@localhost mk]$ ll
total 12
-rw-rw-r--. 1 yyq yyq 56 Sep 27 10:13 main.c
-rw-rw-r--. 1 yyq yyq 106 Sep 27 10:12 mytest.c
-rw-rw-r--. 1 yyq yyq 84 Sep 27 10:09 mytest.h
[yyq@localhost mk]$ gcc mytest.c main.c -o mybin #将2个c文件直接编译
[yyq@localhost mk]$ ll
total 24
-rw-rw-r--. 1 yyq yyq 56 Sep 27 10:13 main.c
-rwxrwxr-x. 1 yyq yyq 8424 Sep 27 10:16 mybin
-rw-rw-r--. 1 yyq yyq 106 Sep 27 10:12 mytest.c
-rw-rw-r--. 1 yyq yyq 84 Sep 27 10:09 mytest.h
2、当文件有很多很多的时候,要用Linux项目自动化构建工具-make/Makefile
# 1、先在当前目录下 创建一个文件 Makefile或者makefile
[yyq@localhost mk]$ touch Makefile #此处用大写打头
[yyq@localhost mk]$ ll
total 12
-rw-rw-r--. 1 yyq yyq 56 Sep 27 10:13 main.c
-rw-rw-r--. 1 yyq yyq 0 Sep 27 10:19 Makefile
-rw-rw-r--. 1 yyq yyq 106 Sep 27 10:12 mytest.c
-rw-rw-r--. 1 yyq yyq 84 Sep 27 10:09 mytest.h
# 2、打开Makefile或者makefile 文件,写入
mybin:mytest.c main.c #要生成的文件名:依赖于那些文件
gcc main.c mytest.c -o mybin
.PHONY:clean
clean:
rm -f mybin #要删除的文件名
还可以这么写,把依赖关系反过来写也可以!先-E,再-S,再-c,再输出可执行文件
mybin:mytest.o
gcc mybin.o -o mybin
mybin.o:
gcc -c mybin.s -o mybin.o
mybin.s:mybin.i
gcc -S mybin.i -o mybin.s
mybin.i:mytest.c
gcc -E mybin.c -o mybin.i
.PHONY:clean
clean:
rm -f mybin.i mybin.s mybin.o mybin #要删除的文件名
##执行的时候是倒序的,因为根据依赖关系会自动寻找。因为执行第一条命令时,需要mybin.o,但没有,就会往下找mybin.s->mybin.i->mybin.c,所以就会先执行-E,再执行-S,再执行-c,最后执行-o。
# 3、执行make命令,生成可执行文件
[yyq@localhost mk]$ make #也可以写为make mybin
gcc main.c mytest.c -o mybin
[yyq@localhost mk]$ ./mybin
hello:0 hello:1 hello:2 hello:3 hello:4 hello:5 hello:6 hello:7 hello:8
# 4、执行make clean命令,清理可执行文件
[yyq@localhost mk]$ make clean
深度剖析Makefile文件
makefile有1、依赖关系;2、依赖方法。通常情况下缺一不可。
mybin:mytest.c main.c #依赖关系 mybin依赖于mytest.c main.c
gcc main.c mytest.c -o mybin #tab+依赖方法
.PHONY:clean # PHONY是makefile的关键字,用这个修饰的叫做伪目标
clean:
rm -f mybin #依赖方法
前提是依赖方法没有改变的情况下:伪目标(make clean)总是被执行的,而(make)第一行目标文件只能执行一次。总是被执行指的是依赖关系是否可以无障碍执行。
make会扫描Makefile文件的第一个命令,就去执行。(make mybin == make),而clean是第二个命令,所以要用make clean。
**为什么gcc知道这个文件是最新的呢(make只能执行一次)?**联系到 文件属性的三个时间stat filename
,modify是指文件内容修改的时间,change是指文件属性(文件权限、文件大小等)被修改的时间,access是访问文件的时间(访问了不会立刻修改时间)。【在文件操作的时候,改文件次数多?还是访问文件的次数多?----当然是访问文件次数多,所以访问时间access的时间被更改的频率太高了,就会进行更多次的IO,即访问磁盘。实际上,我们也不在意访问时间,故设计者采用了一个策略:等访问次数到了一个值,才会更新access时间。】
第一次执行make时,.c文件的modify时间一定要比.exe文件的modify时间早,因为先有.c文件才有可执行文件;
第二次执行make时,.c文件的modify时间一定要比.exe文件的modify时间晚,才能说明.c被修改过,所以可执行文件也要更新,才能执行make命令。如果不存在时间差,执行make的时候,提示如下make: 'filename' is up to date.
,就说明可执行文件已经是最新的了。
所以gcc是依靠 .c和可执行文件(就是依赖关系的这些文件)的modify的时间差来判断。
#####################
故用.PHONY修饰的伪目标实际上就是让make不要根据modify时间差来判断。
//如果改为下面这个代码
.PHONY:mybin ####修改
mybin:mytest.c main.c #依赖关系 mybin依赖于mytest.c main.c
gcc main.c mytest.c -o mybin #tab+依赖方法
.PHONY:clean # PHONY是makefile的关键字,用这个修饰的叫做伪目标
clean:
rm -f mybin #依赖方法
//make就能一直被执行,make clean也一直能被执行
在依赖方法前加上@符号,就可以让语句只执行不输出
mybin:main.o mytest.o
gcc $^ -o $@
main.o:main.c
gcc -c main.c
mytest.o:mytest.c
gcc -c mytest.c
.PHONY:clean
clean:
rm -f main.o mytest.o mybin
------简化------
mybin:main.o mytest.o
gcc $^ -o $@
%.o:%.c
gcc -c $<
.PHONY:clean
clean:
rm -f *.o mybin
# $^表示所有的依赖文件(依赖文件列表) $@表示目标文件
# %c表示当前目录下所有的.c文件,文件名展开 %.o表示对应的.c文件所形成的.o文件
# $<表示 把%.c所代表的源文件一个一个打出来,用gcc进行编译,形成同名.o文件
---------------------
%.o:%.c
gcc -c $<
#相当于展开变成
main.o:main.c #%.o:%.c
gcc -c main.c #$<
mytest.o:mytest.c #%.o:%.c
gcc -c mytest.c #$<
----------------------
实例:进度条
1、缓冲区
#include <stdio.h>
#include <unistd.h>
//1
int main()
{
printf("hello progress bar\n");
sleep(3);//单位是秒
return 0;
}
---------------------------------
//2
int main()
{
printf("hello progress bar");
sleep(3);
return 0;
}
以上两段代码,仅仅是少了个\n的区别。
anyway程序就是自上而下的执行,这个没错!!但是为什么我们看到的是1会先执行printf再sleep,而2先sleep,那printf呢?
答:在缓冲区里面!缓冲区可以先理解为是一块内存区域,那就存在何时刷新的问题,目前的刷新策略又分为:无缓冲(立即刷新)、行缓冲(碰到\n就会把之前的数据一起刷新)、全缓冲(缓冲区很大,只有缓冲区满了才刷新)、程序退出(自动刷新)。很明显缓冲区的刷新策略是行缓冲
程序会先把printf中的数据读入到缓冲区里,遇到\n才刷新,而程序2没遇到\n,只能是程序退出时才刷新。
那我不想用\n,也想让缓冲区立刻刷新应该怎么做呢?用fflush
#include "ProgBar.h"
int main()
{
printf("hello Progress Bar!");
fflush(stdout);
sleep(3);
return 0;
}
2、\r \n
- Unix系统里,每行结尾只有“<换行>”,即“
\n
”; - Windows系统里面,每行结尾是“<换行><回车>”,即“
\n\r
”; - Mac系统里,每行结尾是“<回车>”。
Windows:
‘\r’ 回车,回到当前行的行首,而不会换到下一行,如果接着输出的话,本行以前的内容会被逐一覆盖;
‘\n’ 换行,换到当前位置的下一行,而不会回到行首;
\n //换行,换行+回车,把光标先移到下一行,然后换到行首->也就是下一行的行首
\r //回车,return 回到本行行首,这就会把这一行以前的输出覆盖掉,具体内部细节就像是输出缓冲区重新开始缓冲了一样
Unix系统:
每行结尾只有“<换行>”,即"\n";
Linux:
遇到换行符(‘\n’)会进行回车+换行的操作,回车符反而只会作为控制字符(‘^M’)显示,不发生回车的操作。
当我们采用如下代码时
#include "ProgBar.h"
int main()
{
//1 正常显示
int i = 3;
while(i > 0)
{
printf("%d\n", i);
sleep(1);
i--;
}
//2 正常显示 与\n效果相同
int i = 3;
while(i > 0)
{
printf("%d\r\n", i);
sleep(1);
i--;
}
//3 无法显示
int i = 3;
while(i > 0)
{
printf("%d\r", i);
sleep(1);
i--;
}
printf("hello Progress Bar!");
fflush(stdout);
sleep(3);
return 0;
}
//结果
//1
3
2
1
hello Progress Bar!
//2
3
2
1
hello Progress Bar!
//3
hello Progress Bar!
代码3不会显示任何数字,因为缓冲区是行刷新。要在后面加上fflush(stdout)就好了,只不过是覆盖刷新,3会变成2,再变成1,像倒计时一样。
//3 //2 //1
//hello Progress Bar!
代码3就是我们要用到的进度条设计
//ProgBar.h
1 #pragma once
2
3 #include <unistd.h>
4 #include <stdio.h>
5 #include <string.h>
6
7 #define NUM 101
8
9 void progress_bar();
//main.c
1 #include "ProgBar.h"
2
3 int main()
4 {
5 progress_bar();
6
7 //printf("hello Progress Bar!");
8 //fflush(stdout);
9 //sleep(3);
10 //printf("\n");
11 return 0;
12 }
//ProgBar.c
1 #include "ProgBar.h"
2
3 void progress_bar()
4 {
5 char bar[NUM];
6 memset(bar, '\0', sizeof(bar));
7
8 const char* label = "|/-\\";
9 int i = 0;
10 while(i <= 100)
11 {
12 printf("[%-100s][%-3d%%][%c]\r", bar, i, label[i%4]);
13 fflush(stdout);
14 bar[i++] = '#';
15 usleep(50000);
16 }
17 printf("\n");
18 }