目录
软件包管理器
1>软件包
2>软件生态
3>yum操作
a.查看软件包
b.安装软件
c.卸载软件
4>知识点
vim编辑器
1>基本概念
2>基本操作
3>正常模式命令集
a.模式切换
b.移动光标
c.删除
d.复制
e.替换
f.撤销
g.更改
4>底行模式命令集
a.列出⾏号
b.定位
c.查找
d.退出
e.其它
5>其它实用命令集
6>总结
gcc/g++编译器
1>基本概念
2>编译选项
a.预处理
b.编译
c.汇编
d.链接
3>知识点
4>动静态库
a.库的概念
b.静态库
c.动态库
5>动静态链接
a.静态链接
b.动态链接
6>其它常用选项
自动化创建-make/Makefile
1>基本概念
2>使用和语法
a.简单使用
b.默认扫描
c.伪目标
d.AMC时间
3>推导原则
4>扩展语法
进度条
1>回车与换行
2>行缓冲区
3>倒计时程序
4>进度条代码
版本控制器git
gdb调试器
1>基础知识
2>使用
3>技巧
软件包管理器
1>软件包
为了提高效率,有些人将一些软件(程序的源代码)提前编译好,做成软件包(可以理解成App)放在一个服务器上,通过包管理器(可以理解成应用商店)可以很方便的获取这个编译好的软件包,直接进行安装
yum是Linux下常用的一种包管理器
Ubuntu:主要使用apt作为其包管理器,同样提供了自动解决依赖关系、下载和安装软件包的功能
2>软件生态
Linux下载软件的过程
问题来了,我们的服务器怎么知道,去哪里找软件包?
那是因为在Linux机器上,有对应给yum提供的配置文件!url或者ip地址!
操作系统的好坏评估 --- 生态问题
最根本的是客户群体,因为操作系统针对的客户群体不同,风格以及使用也不同,所以就有了这么多款操作系统(归根结底就是生态的原因)
软件包依赖的问题
yum可以帮助我们完美解决这个问题
国内镜像源
因为大多数软件都是国外的,我国的企业或高校通过购买服务器,将国外的软件包拷贝到国内的服务器,并且提供一个国内的配置文件,后续使用软件只需访问国内的就可以了
切换镜像源,本质就是更改配置文件
下面是我用文心一言生成的切换镜像源的操作,大家也可以尝试使用文心一言或其它工具进行更改配置文件操作
wget获取网页
软件源
配置文件又称为软件源(我们使用的云服务器,内置的配置文件已经是国内的了!)
新开发的软件源要放在扩展软件源,经过时间的验证和认可才可以迁移到稳定软件源
3>yum操作
a.查看软件包
通过yum list命令可以罗列出当前有哪些软件包,使用grep命令筛选我们需要的包
b.安装软件
通过yum命令可以完成gcc-c++的安装,其中包含g++
yum/apt 会自动找到都有哪些软件包需要下载,这时候敲 y 确认安装
出现 complete 字样或中间未发现报错,说明安装完成
注意:
① 安装软件时因为需要向系统目录中写入内容,所以一般需要sudo或者切换到root账户下才能完成
② yum/apt 安装软件只能一个装完了再装另一个,不然会报错
③ Linux一般而言,软件只要安装一次,其它用户都能用!并且是以other的身份使用,毕竟我们是以root身份安装的
c.卸载软件
通过yum命令可以完成对gcc的卸载
在安装或卸载的时候可以加上 -y (sudo yum install/remove -y ...)这样就不需要问我们了,直接安装或卸载
4>知识点
① 操作系统生态问题!
② 什么是OS是好的OS?
③ 重新理解Centos vs Ubnutu vs kail ...
vim编辑器
vi 和 vim 都是多模式编译器,不同的是vim是vi的升级版本,它不仅兼容vi的所有指令,而且还有一些新的特性在里面
1>基本概念
vim有很多种模式,这里我介绍基础且常用的三种模式
① 正常/普通/命令模式(Normal mode)
功能:控制屏幕光标的移动,字符、字或行的删除,移动复制某区段及进入插入模式或末行模式
② 插入模式(Insert mode)
功能:只有在Insert mode下,才可以做文字输入
③ 末行模式(last line mode)
功能:文件保存或退出,也可以进行文件替换,找字符串,列出行号等操作
2>基本操作
① 进入vim全屏幕编辑画面 | 输入vim及文件名称(vim test.c) |
---|---|
② [正常模式]切换至[插入模式] | 「 a 」「 i 」「 o 」 |
③ [插入模式]切换至[正常模式] | 按⼀下「ESC」键切换回[正常模式] |
④ [正常模式]切换至[底行模式] | 「shirt + ;」---> 本质就是输入「 : 」 |
⑤ [底行模式]切换至[正常模式] | 按⼀下「ESC」键切换回[正常模式] |
⑥ 退出vim及保存文件,在[正常模式]下切换至末行模式 | |
输入 「w」 | 保存当前文件 |
输入 「wq」 | 存盘并退出vim |
输入 「q」 | 不存盘强制退出vim |
输入 「wq!」 | 存盘并强制退出vim |
当我们不知道此时处于什么模式时,直接ESc切换回正常模式就可以了
3>正常模式命令集
a.模式切换
从正常模式切换至插入模式 | |
---|---|
「i」 | 从光标当前位置开始输入文件 |
「a」 | 从目前光标所在位置的下一个位置开始输入文字 |
「o」 | 插入新的一行,从行首开始输入文字 |
从插入模式切换至正常模式 | |
「ESc」 | 切换回正常模式 |
「shirt +zz」 | 保存文件并退出vim |
b.移动光标
「h」、「j」、「k」、「l」 | 分别控制光标左、下、上、右移一格 |
---|---|
「gg」 | 进入文本开始 |
「shirt + g」->「G」 | 进入文本末端 |
「n + gg」 | 进入文本第n行 |
「shirt + 4」->「$」 | 移动到光标所在行的行尾 |
「shirt + 6」->「^」 | 移动到光标所在行的行首 |
「w」 | 光标跳到下个单词的开头 |
「e」 | 光标跳到下个单词的尾端 |
「b」 | 光标回到上个单词的开头 |
「n + l」 | 光标移到该行的第n个位置 |
「ctrl + d」 | 屏幕往前移动半页 |
「ctrl + u」 | 屏幕往后移动半页 |
「ctrl + f」 | 屏幕往前移动一页 |
「ctrl + b」 | 屏幕往后移动一页 |
c.删除
「x」 | 每按⼀次删除光标所在位置的⼀个字符 |
---|---|
「n + x」 | 删除光标所在位置后面的n个字符(包括自己) |
「shirt + x」->「X」 | 每按⼀次删除光标所在位置的前⾯⼀个字符 |
「n + X」 | 删除光标所在位置前⾯的n个字符(包括自己) |
「dd」 | 剪切/删除光标所在⾏ |
「n + dd」 | 从光标所在⾏开始剪切/删除n⾏ |
d.复制
「yw」 | 将光标所在之处到字尾的字符复制到缓冲区中 |
---|---|
「n + yw」 | 复制n个字到缓冲区 |
「yy」 | 复制光标所在⾏到缓冲区 |
「n + yy」 | 拷⻉从光标所在⾏往下数n⾏⽂字 |
「p」 | 将缓冲区内的字符贴到光标所在位置 |
「n + p」 | 粘贴到当前行的下n行 |
注意:所有与 y 有关的复制命令都必须与 p 配合才能完成复制与粘贴功能
e.替换
「r」 | 替换光标所在处的字符 |
---|---|
「n + r」 | 替换光标所在处及后n个字符 |
「shirt + r」->「R」 | 替换光标所到之处的字符,直到按下「ESC」键为⽌ |
「shirt + `」->「~」 | 快速大小写切换 |
f.撤销
「u」 | 误执⾏⼀个命令时,u可以回到上⼀个操作 |
---|---|
「ctrl + r」 | 撤销的恢复 |
注意:只要不退出vim,都可以撤销!
g.更改
「cw」 | 更改光标所在处的字到字尾处 |
---|---|
「c + n + w」 | 更改光标所在处及后面n个字 |
4>底行模式命令集
a.列出⾏号
「set nu」 | 在⽂件中的每⼀⾏前⾯列出⾏号 |
---|
b.定位
「n」 | 在冒号后输⼊⼀个数字,再按回⻋键就会跳到该⾏了 |
---|
c.查找
「/关键字」 | 先按「/」键,再输⼊您想寻找的字符,如果第⼀次找的关键字不是您想要的,可以⼀直按「n」会往后寻找到您要的关键字为⽌ |
---|---|
「?关键字」 | 先按「?」键,再输⼊您想寻找的字符,如果第⼀次找的关键字不是您想要的,可以⼀直按「n」会往前寻找到您要的关键字为⽌ |
d.退出
「q」 | 退出vim |
---|---|
「q!」 | 强制退出vim |
「w」 | 将⽂件保存起来 |
「w!」 | 强制将⽂件保存起来并退出vim |
e.其它
「!command」 | 输入(!指令),会先执行这条指令,按任意键再返回来,这样可以方便调试 |
---|---|
「vs (other)」 | 这里的other指的是其它文件,实现分屏操作 |
「ctrl + ww」 | 在分屏时让光标移动到任意屏幕 |
注意:光标在哪个终端,执行的就是哪个终端
5>其它实用命令集
① 批量化注释:ctrl+v --> hjkl --> shirt+i == I --> ESc
解释:ctrl+v是进入视图模式,hjkl选择需要注释的行,shirt+i是进入插入模式,最后ESc退出视图模式(退出后才会显示注释的全部内容)
② 批量化注释:ctrl+v --> hjkl --> d
解释:ctrl+v是进入视图模式,hjkl选择需要去注释的内容,d是删除该内容
③ 替换(底行模式):%s/printf/print/g
解释:这条指令的意思是将全部的printf换成print,g代表的意思是全部,注意是后替换前
④ 快速定位:当我们编译报错时,根据报错提示知道大概哪里错误,这时就可以输入 vim test.c +13
解释:这条指令的意思是进入vim之后光标直接定位到第13行这个位置
⑤ 便捷指令:!commond
解释:这条指令的意思是快速执行历史命令,例如gcc test.c -o test,我修改了一下test.c的内容,此时 !g 就可以快速帮我再次执行这条指令;同样 !l 可以快速帮我执行 ll 这条指令
6>总结
gcc/g++编译器
1>基本概念
预处理 | 进⾏宏替换/去注释/条件编译/头⽂件展开等 |
---|---|
编译 | ⽣成汇编 |
汇编 | ⽣成机器可识别代码 |
链接 | ⽣成可执⾏⽂件或库⽂件 |
2>编译选项
格式:gcc [选项] 要编译的文件 [选项] [目标文件]
a.预处理
① 预处理功能主要包括宏定义,⽂件包含,条件编译,去注释等
② 预处理指令是以#号开头的代码⾏
③ -E 选项是让 gcc 在预处理结束后停⽌编译过程
④ -o 选项是指⽬标⽂件,".i" ⽂件为已经预处理的C原始程序
b.编译
① 在这个阶段中,gcc ⾸先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的⼯作,在检查⽆误后,gcc 把代码翻译成汇编语⾔
② -S 选项是只进⾏编译⽽不进⾏汇编,⽣成汇编代码
c.汇编
① 汇编阶段是把编译阶段⽣成的“.s”⽂件转成⽬标⽂件
② -c 选项是可以看到汇编代码已转化为“.o”的⼆进制⽬标代码
d.链接
① 在成功编译之后,就进⼊了链接阶段
3>知识点
① 为什么要编译、汇编?
减少语言开发的成本
② 如何理解条件编译?
通过条件编译就可以只需要维护一份代码,实现不同版本(例如免费版、校园版、专业版...)
4>动静态库
⼀般我们的云服务器,C/C++的静态库并没有安装,可以采⽤如下⽅法安装(我这里是安装了的,所以显示是有这个库的)
yum install glibc-static libstdc++-static -y
a.库的概念
我们的C程序中,并没有定义 “printf” 的函数实现,且在预编译中包含的 “stdio.h” 中也只有该函数的声明,⽽没有定义函数的实现,那么是在哪⾥实现 “printf” 函数的呢?
答案是系统把这些函数实现都被做到名为 libc.so.6 的库⽂件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径 “/usr/lib” 下进⾏查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数 “printf” 了,⽽这也就是链接的作⽤
b.静态库
静态库是指编译链接时,把库⽂件的代码全部加⼊到可执⾏⽂件中,因此⽣成的⽂件⽐较⼤,但在运⾏时也就不再需要库⽂件了(其后缀名⼀般为 “.a” )
c.动态库
动态库与之相反,在编译链接时并没有把库⽂件的代码加⼊到可执⾏⽂件中,⽽是在程序执⾏时由运⾏时链接⽂件加载库,这样可以节省系统的开销。(其后缀名⼀般为 “.so”)
Linux | 动态库XXX.so, 静态库XXX.a |
---|---|
Windows | 动态库XXX.dll, 静态库XXX.lib |
5>动静态链接
gcc默认⽣成的可执行程序是动态链接的!(这点可以通过 file 命令验证)
a.静态链接
在我们的实际开发中,不可能将所有代码放在⼀个源⽂件中,所以会出现多个源⽂件,⽽且多个源⽂件之间不是独⽴的,⽽会存在多种依赖关系,如⼀个源⽂件可能要调⽤另⼀个源⽂件中定义的函数,但是每个源⽂件都是独⽴编译的,即每个.c⽂件会形成⼀个.o⽂件,为了满⾜前⾯说的依赖关系,则需要将这些源⽂件产⽣的⽬标⽂件进⾏链接,从⽽形成⼀个可以执⾏的程序
优点 | 缺点 |
---|---|
在可执⾏程序中已经具备了所有执⾏程序所需要的任何东西,在执⾏的时候运⾏速度快 | 浪费空间:因为每个可执⾏程序中对所有需要的⽬标⽂件都要有⼀份副本,所以如果多个程序对同⼀个⽬标⽂件都有依赖,如多个程序中都调⽤了printf()函数,则这多个程序中都含有printf.o,所以同⼀个⽬标⽂件都在内存存在多个副本 |
更新⽐较困难:因为每当库函数的代码修改了,这个时候就需要重新进⾏编译链接形成可执⾏程序 |
b.动态链接
基本思想是把程序按照模块拆分成各个相对独⽴部分,在程序运⾏时才将它们链接在⼀起形成⼀个完整的程序,⽽不是像静态链接⼀样把所有程序模块都链接成⼀个单独的可执⾏⽂件(并且动态链接远⽐静态链接要常⽤得多)
总结:
① 动态库是共享的,所以不能丢失,一旦丢失,所有依赖动态库的程序都会运行出错
② 静态链接是把我们要的库方法实现,直接拷贝到我们的可执行程序中
6>其它常用选项
-E | 只激活预处理,这个不⽣成⽂件,需要我们把它重定向到⼀个输出⽂件⾥⾯ |
---|---|
-S | 编译到汇编语⾔不进⾏汇编和链接 |
-c | 编译到⽬标代码 |
-o | ⽂件输出到⽂件 |
-static | 对⽣成的⽂件采⽤静态链接 |
-g | ⽣成调试信息(GNU 调试器可利⽤该信息) |
-w | 不⽣成任何警告信息 |
-Wall | ⽣成所有警告信息 |
-shared | 尽量使⽤动态库,所以⽣成⽂件⽐较⼩,但是需要系统有动态库 |
-O0、-O1、-O2、-O3 | 编译器的优化选项的4个级别,-O0表⽰没有优化,-O1为缺省值,-O3优化级别最⾼ |
自动化创建-make/Makefile
1>基本概念
① ⼀个⼯程中的源⽂件不计数,其按类型、功能、模块分别放在若⼲个⽬录中,makefile定义了⼀系列的规则来指定,哪些⽂件需要先编译,哪些⽂件需要后编译,哪些⽂件需要重新编译,甚⾄于进⾏更复杂的功能操作
② makefile带来的好处就是⸺“⾃动化编译”,⼀旦写好,只需要⼀个make命令,整个⼯程完全⾃动编译,极⼤的提⾼了软件开发的效率
③ make是⼀个命令⼯具,是⼀个解释makefile中指令的命令⼯具,⼤多数的IDE都有这个命令
④ make是⼀条命令,makefile是⼀个⽂件,两个搭配使⽤,完成项⽬⾃动化构建!
2>使用和语法
a.简单使用
b.默认扫描
c.伪目标
项目清理
① ⼯程是需要被清理的
② 像clean这种,没有被第⼀个⽬标⽂件直接或间接关联,那么它后⾯所定义的命令将不会被⾃动执⾏,不过我们可以显⽰ “make clean” 执⾏,以此来清除所有的⽬标⽂件,以便重编译
③ ⼀般我们这种clean的⽬标⽂件,都将它设置为伪⽬标,⽤ .PHONY 修饰(伪⽬标的特性是总是被执⾏的!)
d.AMC时间
没有 .PHONY 修饰的目标文件为什么第二次编译就不可以,是如何做到的?其实主要是为了提高编译效率
总结:
① Modify: 内容变更,时间更新
② Change:属性变更,时间更新
③ Access:常指的是⽂件最近⼀次被访问的时间(在Linux的早期版本中,每当⽂件被访问时,其atime都会更新,这种机制会导致⼤量的IO操作)
④ .PHONY:让make忽略源⽂件和可执⾏⽬标⽂件的M时间对⽐
3>推导原则
下面是推导原则
在默认的⽅式下,make是如何⼯作的?
make会在当前⽬录下找名字叫“Makefile”或“makefile”的⽂件 |
---|
如果找到,它会找⽂件中的第⼀个⽬标⽂件(target),在上⾯的例⼦中,他会找到 myproc 这个⽂件,并把这个⽂件作为最终的⽬标⽂件 |
如果 myproc ⽂件不存在,或是 myproc 所依赖的后⾯的 myproc.o ⽂件的⽂件修改时间要⽐ myproc 这个⽂件新(可以⽤ touch 测试),那么它就会执⾏后⾯所定义的命令来⽣成 myproc 这个⽂件 |
如果 myproc 所依赖的 myproc.o ⽂件不存在,那么 make 会在当前⽂件中找⽬标为 myproc.o ⽂件的依赖性,如果找到则再根据那⼀个规则⽣成 myproc.o ⽂件。(这有点像⼀个堆栈的过程) |
当然,我们的C⽂件和H⽂件肯定是存在的,于是 make 会⽣成 myproc.o ⽂件,然后再⽤ myproc.o ⽂件声明 make 的终极任务,也就是执⾏⽂件了 |
这就是整个make的依赖性,make会⼀层⼜⼀层地去找⽂件的依赖关系,直到最终编译出第⼀个⽬标⽂件 |
在找寻的过程中,如果出现错误,⽐如最后被依赖的⽂件找不到,那么make就会直接退出,并报错,⽽对于所定义的命令的错误,或是编译不成功,make根本不理 |
make只管⽂件的依赖性,意思就是如果在我找了依赖关系之后,冒号后⾯的⽂件还是不在,那么对不起,我就不⼯作了 |
下面是推导过程
4>扩展语法
BIN=proc.exe # 定义变量
CC=gcc
#SRC=$(shell ls *.c) # 采用shell命令行方式,获取当前所有.c文件名
SRC=$(wildcard *.c) # 或者使用 wildcard 函数,获取当前所有.c文件名
OBJ=$(SRC:.c=.o) # 将SRC的所有同名.c替换成为.o形成目标文件列表
LFLAGS=-o # 链接选项
FLAGS=-c # 编译选项
RM=rm -f # 引入命令
$(BIN):$(OBJ)
@$(CC) $(LFLAGS) $@ $^ # $@:代表目标文件名 $^: 代表依赖文件列表
@echo "linking ... $^ to $@"
%.o:%.c # %.c 展开当前目录下所有的.c %.o: 同时展开同名.o
@$(CC) $(FLAGS) $< # %<: 对展开的依赖.c文件,⼀个⼀个的交给gcc
@echo "compling ... $< to $@" # @:不回显命令
.PHONY:clean
clean:
$(RM) $(OBJ) $(BIN) # $(RM): 替换,用变量内容替换它
.PHONY:test
test:
@echo $(SRC)
@echo $(OBJ)
注意:快速创建/删除文件
touch 文件名{1..数量}后缀
rm 文件名{1..数量}后缀
进度条
1>回车与换行
回车 '/r' :将光标或打印位置移至当前行开头,不换到下一行
换行 '/n':将光标或打印位置移至下一行行首,不改变水平位置
2>行缓冲区
#include <stdio.h>
#include <unistd.h>
int main()
{
// printf("hello tianci\n");
// printf("hello tianci");
// fflush(stdout);
sleep(3);
return 0;
}
3>倒计时程序
#include <stdio.h>
#include <unistd.h>
int main()
{
int cnt = 10;
while(cnt >= 0)
{
printf("%-2d\r", cnt);
fflush(stdout);
cnt--;
sleep(1);
}
printf("\n");
return 0;
}
4>进度条代码
先小试牛刀,可以尝试简单编写,没有问题再继续
接下来就是进度条代码的编写
先说一下设计的想法,就是预留100个字符,每走过的位置留下=,右边第一个[]显示百分比(完成了多少),第二个[]显示标识符(一直旋转直到进度条运行结束)
Makefile
BIN=process #程序名和我们的要实现的功能有关,这就有意义了
# SRC=$(shell ls *.c)
SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)
CC=gcc
RM=rm -f
$(BIN):$(OBJ)
@$(CC) $^ -o $@
@echo "链接 $^ 成 $@"
%.o:%.c
@$(CC) -c $<
@echo "编译... $< 成 $@"
.PHONY:clean
clean:
@$(RM) $(OBJ) $(BIN)
.PHONY:test
test:
@echo $(BIN)
@echo $(SRC)
@echo $(OBJ)
process.h
#pragma once
#include <stdio.h>
// v1
void process();
// v2
void FlushProcess(const char*, double total, double current);
注意:头文件中的参数名和实现文件中的参数名可以不一致(头文件中的参数名主要是为了提供可读性,而实现文件中的参数名是实际使用的变量名,只要参数的类型和顺序保持一致就可以了)
process.c
#include "process.h"
#include <string.h>
#include <unistd.h>
#define SIZE 101
#define STYLE '='
// v2:根据进度,动态刷新一次进度条
void FlushProcess(const char* tips, double total, double current)
{
const char* lable = "|/-\\";
size_t len = strlen(lable);
static int index = 0;
char buffer[SIZE];
memset(buffer, 0, sizeof(buffer));
double rate = current * 100.0 / total;
int num = (int)rate;
int i = 0;
for (; i < num; i++)
buffer[i] = STYLE;
printf("%s...[%-100s][%.1lf%%][%c]\r", tips, buffer, rate, lable[index++]);
fflush(stdout);
index %= len;
if (num >= 100) printf("\n");
}
// v1:展示进度条基本功能
void process()
{
int rate = 0;
char buffer[SIZE];
memset(buffer, 0, sizeof(buffer));
const char* lable = "|/-\\";
size_t len = strlen(lable);
}
注意:
① 当网速为0时,进度条虽然停止,但是lable还在一直动,可以知道进度条还在运行,而是网速不行,所以这里的lable就起到了标识符的作用
② memset是因为我想将整个buffer里面都设置为0,后面要增加内容直接增加就行了
③ -100%s --> 100是为了占位置,让右括号直接定位到结尾,不用跟着跑,负号是为了让字符左对齐
④ %% 输出为一个%,//输出为一个/
main.c
#include "process.h"
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
typedef void (*call_t)(const char*, double, double);
double total = 1024.0;
// double speed = 1.0;
double speed[] = {1.0, 0.5, 0.3, 0.02, 0.1, 0.01}; // 模拟网速
// 回调函数
void DownLoad(int total, call_t cb)
{
srand(time(NULL));
double current = 0.0;
while (current <= total)
{
cb("下载中", total, current); // 进行回调
if (current >= total) break;
int random = rand() % 6;
usleep(5000); // 充当下载数据
current += speed[random];
if (current >= total)
current = total;
}
}
void uploadLoad(int total, call_t cb)
{
srand(time(NULL));
double current = 0.0;
while (current <= total)
{
cb("上传中", total, current); // 进行回调
if (current >= total) break;
int random = rand() % 6;
usleep(5000); // 充当下载数据
current += speed[random];
if (current >= total)
current = total;
}
}
int main()
{
DownLoad(1024.0, FlushProcess);
printf("download 1024.0MB done\n");
DownLoad(512.0, FlushProcess);
printf("download 512.0MB done\n");
DownLoad(256.0, FlushProcess);
printf("download 256.0MB done\n");
DownLoad(128.0, FlushProcess);
printf("download 128.0MB done\n");
DownLoad(64.0, FlushProcess);
printf("download 64.0MB done\n");
uploadLoad(500.0, FlushProcess);
// process();
return 0;
}
版本控制器git
为了能够更⽅便我们管理这些不同版本的⽂件,便有了版本控制器。所谓的版本控制器,就是能让你了解到⼀个⽂件的历史,以及它的发展过程的系统(通俗的讲就是⼀个可以记录⼯程的每⼀次改动和版本迭代的⼀个管理系统,同时也⽅便多⼈协同作业)
⽬前最主流的版本控制器就是 Git,它可以控制电脑上所有格式的⽂件,最重要的是可以帮助我们管理软件开发项⽬中的源代码⽂件!
// 安装
sudo yum install git
这里有几个地方需要注意:
① git提交的时候,只会提交变化的部分!
② 首次使用git需要设置名字和邮箱(有提示,git config --global user.name/user.email "...")
③ git版本管理,只进行管理源文件
④ .gitignore:需要忽略的特定后缀的文件列表(vim .gitignore 修改配置后立即生效)
⑤ 远端仓库,相比较与任何人,都是最新的(大胆pull,放心!)
⑥ 为什么要产生冲突?那是因为想提醒本地用户,你该和远程仓库进行同步了(可以理解为一种保护机制)
gdb调试器
1>基础知识
2>使用
先来看一下简单的使用
cgdb调试起来会清晰一点,指令和gdb是一样的
sudo yum install -y cgdb
总结:
开始:gdb binFile
退出:ctrl + d 或 quit 调试命令
命令 | 作⽤ |
---|---|
list/l | 显⽰源代码,从上次位置开始,每次列出10⾏ |
list/l 函数名 | 列出指定函数的源代码 |
list/l ⽂件名:⾏号 | 列出指定⽂件的源代码 |
r/run | 从程序开始连续执⾏ |
n/next | 单步执⾏,不进⼊函数内部 |
s/step | 单步执⾏,进⼊函数内部 |
break/b [⽂件名:]⾏号 | 在指定⾏号设置断点 |
break/b 函数名 | 在函数开头设置断点 |
info break/b | 查看当前所有断点的信息 |
finish | 执⾏到当前函数返回,然后停⽌ |
print/p 表达式 | 打印表达式的值 |
p 变量 | 打印指定变量的值 |
set var 变量=值 | 修改变量的值 |
continue/c | 从当前位置开始连续执⾏程序 |
delete/d breakpoint | 删除所有断点 |
delete/d breakpoints n | 删除序号为n的断点 |
disable breakpoints | 禁⽤所有断点 |
enable breakpoints | 启⽤所有断点 |
info/i breakpoints | 查看当前设置的断点列表 |
display 变量名 | 跟踪显⽰指定变量的值(每次停⽌时) |
undisplay 编号 | 取消对指定编号的变量的跟踪显⽰ |
until X⾏号 | 执⾏到指定⾏号 |
backtrace/bt | 查看当前执⾏栈的各级函数调⽤及参数 |
info/i locals | 查看当前栈帧的局部变量值 |
quit | 退出GDB调试器 |
3>技巧
watch
执⾏时监视⼀个表达式(如变量)的值。如果监视的表达式在程序运⾏期间的值发⽣变化,gdb 会暂停程序的执⾏,并通知使⽤者
注意:如果你有⼀些变量不应该修改,但是你怀疑它修改导致了问题,你可以watch它,如果变化了,就会通知你
set val
更改⼀下标志位
条件断点
注意:
① 新增:b ⾏号/⽂件名:⾏号/函数名 if i == 30(条件)
② 给已有断点追加:condition 2 i==30, 其中2是已有断点编号,没有if
③ cgdb分配操作按ESc进入代码屏,输入i回到cgdb屏
本篇文章到这里就结束啦,希望这些内容对大家有所帮助!
下篇文章见,希望大家多多来支持一下!
感谢大家的三连支持!