一、yum工具
1.yum 背景知识
(1)商业生态
在安装软件之前,需要先下载对应的软件的安装包,但是这个安装包并不存在于本地的计算机磁盘,而是存在于远端服务器上。世界上的服务器多了,那么计算机是如何知道该软件存在于哪个具体的服务器上呢?
对于电脑来说,我们一般通过官网下载相应软件包;而对于手机来说,我们更多通过手机自带的应用商店来下载软件包。
但实际上应用商店这个软件就那么大,里面也不存在所有的软件包。所以应用商店只是储存着对应软件下载网站的链接,最后我们的软件包还是从对应的网站上下载的。
这些我们下载下来的软件包是由一些企业、组织或个人提供的。这些企业、组织以及个人为了某种利益编写出了软件包,然后将其放在了对应的服务器上,供用户下载使用。
自此,一条简单的商业生态链就出来了:
一部分人编写出了软件包供其他人使用,并通过收费或者内置广告等方式从中获取利益;同时,它们也向手机厂商或者第三方下载软件的公司支付费用,使得自己的软件能够出现在对应品牌手机的应用商店(比如:苹果AppStore)或者第三方的下载工具 (比如迅雷等) 中被下载使用。
(2)开源生态
有人编写软件是为了赚钱,自然也有的人不为赚钱。有的人会将自己编写出的软件包的源代码公开,让别人能够免费使用,这种就叫开源。
yum是Linux下常用的一种包管理器;主要应用在 Fedora, RedHat,Centos 等Linux发行版本上,软件包和yum, 就相当于手机中的 “App的安装包” 和 “应用商店软件” 这样的关系。
在Linux之父托瓦兹编写出了Linux操作系统并将其开源之后,越来越多的人参与到了Linux的完善与扩展中来,其中也不乏为Linux免费编写软件的人;这些人会加入相应的Linux社区,然后将自己编写的软件放在社区对应的服务器上;而不同的社区会在自己的Linux版本中内置服务器和软件对应的下载链接,而这个用于存放下载链接的软件就是yum。
上面的过程也是开源生态的形成过程 – 不同的Linux发行版创建对应的Linux社区,社区拥有一个公共账户,用于接受来自世界各地的捐款,然后用账户里面的资金购买服务器、举行各种活动等等;来自世界各地的具有开源精神的人将自己编写的开源软件部署在社区的服务器上;社区再将这些软件的下载链接拷贝到对于Linux发行版的软件包管理器 (比如yum) 中,使得我们能够在Linux中通过软件包管理器安装使用各种软件。
(3)软件生态本土化
由于西方国家在计算机方面起步与发展比我国要早很多,所以我们上面所说的开源生态最先在西方形成,即大多数Linux社区,包括社区对应的服务器都是部署在国外的;所以在国内通过链接下载软件时访问会比较慢,再加上我国自有国情在,毕竟梯子这种东西是违法的,有时候想下载外网的软件还会访问失败。
针对上面这种情况,我国的一些高校以及公司就镜像(复制粘贴再加部分修改)了国外的软件服务,即把国外服务器上的软件拷贝到了国内的服务器上,使得我们可以直接访问国内的服务器就可以下载对应的软件。但是只拷贝软件也还不行,因为 yum 中下载软件时默认访问的链接还是国外的,所以这些高校/公司还提供了一套国内的下载链接配置文件 – yum 源配置文件。
在Linux中,yum 源配置文件是存在于 /etc/yum.repos.d/ 目录下的 CentOS-Base.repo 文件
如果你使用的是云服务器,那么 yum 源一般都是配置好的,如果你使用的是虚拟机,你需要打开 CentOS-Base.repo 查看里面的链接是否是国内的链接,如果不是,就需要自己手动配置,配置的方法网上很多,直接搜索对应的Linux发行版即可。
这里的镜像就都是腾讯网站下的安装包(tencentyum)
2.yum 的基本使用
(1)查看软件包
我们可以通过 yum list 命令列举出当前一共有哪些软件包可供安装(那个显示可安装程序的动图太大了传不上来……)
但软件包的数目太多, 所以我们一般使用 grep 命令来筛选出我们需要的包,比如找到rzsz
(2)软件包名称构成
主版本号.次版本号.源程序发行号-软件包的发行号.主机平台.cpu架构
- “.x86_64” 后缀表示64位系统的安装包,“.i686” 后缀表示32位系统安装包,安装包要和系统匹
- “el7” 表示主机平台,也就是操作系统发行版的版本,其中 “el7” 表示的是 centos7/redhat7,“el6” 表示centos6或者redhat6
- 最后一列中@OS表示的是 “软件源” 的名称, 类似于 “小米应用商店”, “华为应用商店”
(3)安装软件
我们可以通过如下命令来安装软件包 (其中 -y 代表不询问直接安装)
格式:yum install -y 软件名
rzsz 工具用于 windows 机器和远端的 Linux 机器通过 XShell 传输文件,安装完毕之后我们可以通过拖拽或者命令的方式将文件上传过去。
输入yum install -y rzrs,开始安装这个软件。不过这里告诉我们这个软件已经被安装好了,不需要再次安装。
rzrs在我们官方软件的列表中,但是官方软件的列表只包含了一部分经过测试的运行稳定可靠的软件。有一些软件并没有被纳入 Centos、Ubuntu、Kail等相关生态平台的官方软件集合中,如果我们想使用这些软件,需要安装 非官方软件集合列表:epel-realse
yum install -y epel-realse进行安装。不过这里告诉我们已经安装了最新版本,不需要再次安装。
我们再次安装一个不在官方软件列表中的sl软件程序,它可以在屏幕上打印一列小火车。
yum install -y sl
注意事项
- 安装软件时由于需要向系统目录中写入内容,一般需要 sudo 或者切到 root 账户下才能完成
- yum安装软件只能一个装完了再装另一个;正在yum安装一个软件的过程中,如果再尝试用yum安装另外一个软件,yum会报错
- 软件和软件之间是有关联的,即具有一定的耦合度; yum 为了解决软件之间相互依赖的问题,有时候在安装一个软件会连带安装其他一些软件
(4)卸载软件
卸载软件的指令如下 (其中 -y 代表不询问直接卸载):
yum remove -y 软件名
我们卸载掉之前安装的sl小火车
注意:关于 yum 的所有操作必须保证主机 (虚拟机) 网络畅通;可以通过 ping 指令验证网络:
二、vim编辑器
1.认识vim
vim是Linux 系统上一款强大的文本编辑器,类似于我们windows里的记事本。由于使用它便捷,高效的特点使它成为了目前最强大的编辑器,但是它最大的缺点之一就是使用门槛较高。
2.模式及其切换
通过vim 文件名的指令(例如:vim test.c)可以用vim编辑这个文件。
vim有很多的模式,不同的模式适配于不同的操作,vim有很多很多的模式,我们主要涉及到五六个,我们优先讲解命令模式、插入模式和底行模式三个。
当打开vim时,默认是命令模式,此时即使我们敲击键盘,也并没有输入内容。
如果想要输入内容,我们需要在键盘上敲 Shift+i 切换到插入模式。(左下角的INSERT表示插入模式)在这个模式下,我们就可以像记事本一样输入字符了。
此时,我们在插入模式下编写好了一个程序,我们想要保存并退出需要先按左上角esc退回到命令模式,然后按 Shift+:进入底行模式。
底行模式下输入wq就可以保存并退出了。
vim模式的切换一定是在命令模式下切换到其他模式,如果我们想从一个非命令模式切换到另一个非命令模式,我们需要按esc退回到命令模式再进入其他模式,不同的非命令模式不能直接切换。
详情见下图:
2.插入模式
插入模式允许我们像记事本一样输入内容,调节光标位置使用上下左右键。
Shift+i:从命令模式进入插入模式
Shift+a:从命令模式进入插入模式并将光标移动到最后一个字符后
Shift+o:从命令模式进入插入模式并换行
3.命令模式
(1)光标定位操作
- Shift+4($)让光标快速移动到这一行的结尾。(一行的最右侧)
- Shift+6(^)让光标快速移动到这一行的开头。(一行的最左侧)
- Shift+g(G)让光标快速移动到文件的最后一行开头。(底部开头)
- g+g让光标快速回到文件第一行开头。(顶部开头)
- 行数+Shift+g跳转到任意行的开头。
- h左、j下、k上、l右(逐字符,支持n操作,比如说输入5+l就是跳转到本行的第五个字符前)
- w向后、b向前按单词移动(逐单词,也支持n操作,比如说输入5+w就是跳过五个单词)(但这个单词不一定是英语的单词,比如说符号“}”也被视作一个单词,大概了解一下就好)
- cw删除当前单词,并切换为插入模式,cnw删除n个单词,并切换为插入模式
(2)文本复制、粘贴、剪切、撤销
- yy复制光标所在行,nyy复制多行
- p粘贴,np粘贴多次
- u撤销,ctrl+r取消撤销
- dd剪切光标所在行,ndd剪切多行,ndd+p剪切粘贴,如果我们不粘贴这里的dd就相当于删除
- x向后剪切,nx向后剪切n个;X向前剪切,nX向前剪切n个字符
(3)文本操作
将光标当前位置逐字进行大小写转换(从光标所在位开始,按一次转换一个字符到行末)- R转换为替换模式进行批量化替换
- r单个字符替换,nr对n个字符进行相同的替换
4.底行模式
(1)基本操作
- set no 显示行号
- set nonu 隐藏行号;
- wq! 强制保存并退出,w表示保存,q表示退出,!表示强制且写在字母后面,三个可以自由组合但是单独的!没有意义
- :help vim-modes 查看vim的模式
(2)分屏编辑
- vs copy.c 分屏编辑另一个文件
- ctrl ww 光标在分屏间切换
(3)不退出vim,执行Linux指令
- !ls 在vim中使用ls指令
- !gcc text.c 使用gcc编译text.c生成a.out文件
- !./a.out 运行s.out文件
(4)全局替换与查找
- %s/字符串1/字符串2/g(例如:%s/printf/cout/g)
s代表替换,g代表全局。例子中表示将所有printf替换为cout。
- /查找的内容(例如:/printf)
使用/加查找内容即可进行内容的全局查找,例子中表示查找全局范围内所有的printf
5.vim配置
(1)vim配置的原理
我们之前看到有一个指令叫做:set no用于显示行号。如果我们想让vim默认就打开行号就需要对vim进行配置。
每一个用户在自己的家目录下,有一个属于自己的配置文件,所有的配置都写在这个文件内,叫做.vimrc(隐藏文件,需要自己创建)。所以,虽然每一个用户都使用同一个vim软件,但是不同用户使用自己的vim配置,互不影响。
例如在.vimrc中新增行号功能,可以在.vimrc中写入set nu,保存退出。再次打开vim就加入行号了
(2)vim一键配置
可以在gitee中搜索VimForCpp后,将该链接直接复制粘贴在shell中执行,即可完成配置。
curl -sLf https://gitee.com/HGtz2222/VimForCpp/raw/master/install.sh -o ./install.sh && bash ./install.sh
配置前的vim十分难用,配置后的vim就方便很多了。
不过这个vim配置中换行默认缩进两个空格,如果想改变为4个空格的话可以把这三行的数字的2改为4(我的已经改过了)
6.将普通用户添加至信任列表
(1)使用root用户登录
(2)使用vim打开/etc/sudoers
(3)找到大约100行
(4)复制上面的内容到下一行然后修改头部的用户
(5)底行模式输入w!强制保存,然后输入q!强制退出
这样我们的example用户就被添加到信任账户后,普通用户也可使用sudo提权。
三、gcc/g++编译器
1.程序的编译和运行
一个C程序的编译和运行要经过下列步骤:
预处理(每个C语言源程序进行头文件的包含、注释的删除与宏的替换)→ 编译(每一个文件生成汇编代码)→ 汇编(生成机器可识别代码)→ 连接(生成可执行文件或库文件)
详情请见博客:(1条消息) 程序的编译和运行_聪明的骑士的博客-CSDN博客_编译运行
2.使用gcc工具完成操作
格式:gcc+[选项]+要编译的文件+[选项]+[目标文件]
(1)预处理
预处理功能主要包括宏定义、文件包含、条件编译和删除注释等。
格式:gcc+-E+文件+-o+[目标文件]
实例: gcc –E test.c –o test.i
- “-E”是让 gcc 在预处理结束后就停止,注意必须是大写。
- “-o”是指目标文件,“.i”文件为已经过预处理的C原始程序。
输入指令后ll可以看到新出现了一个test.i的文件,这就是test.c经过预处理后的文件
我们用vim打开test.i和test.c,发现stdio.h的内容已经被拷贝到文件中了,注释条件编译等也不在了
(2)编译(生成汇编)
在编译阶段,gcc 首先要检查代码的规范性、语法的正确性等,以确定代码的实际要做的工作,在检查无误后,gcc 会把代码翻译成汇编语言。
格式:gcc+-S+文件+-o+[目标文件]
实例: gcc –S test.i –o test.s
- “-S”表示整个过程截止到编译结束,注意必须是大写
输入指令后ll可以看到新出现了一个test.s的文件,这就是test.c经过预处理后再编译后的文件
用vim打开形成的test.s和test.c,我们不需要看懂汇编代码,大概其知道其中的一些指令比如说:call是函数调用,push是压栈等就足够了。
(3)汇编(生成机器码)
在汇编阶段,gcc把编译阶段生成的“.s”文件转成目标文件,此时的文件都由01组成,是计算机可以看懂的二进制文件。
格式:gcc+-c+文件+-o+[目标文件]
实例: gcc –c test.s –o test.o
- “-c”表示整个过程截止到编译结束
同样用vim打开test.o文件,内部全是乱码,这是因为在形成二进制机器码的阶段会有加密
(4)连接(生成可执行文件或库文件)
在成功编译之后,就进入了链接阶段,连接起不同的.o文件最终形成可执行程序。
格式:gcc+文件+-o+[目标文件]
实例: gcc test.o –o test
链接后,运行生成的test可执行程序
3.函数库
(1)对链接的新认识
在我们的C程序中,并没有定义 printf 的函数实现,那我们是怎样使用 printf 函数的呢?
你可能会说,是因为在预处理中 stdio.h 的内容被复制到文件中,头文件中包含了这个函数的定义。
但是,其实 stdio.h 头文件中也是只有 printf 函数的声明,没有定义函数的定义,那到底是在哪里定义的“printf”函数的呢?
其实,系统把这些函数的定义都放到了 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是相当于链接到 libc.so.6 库函数中去,这样就能找到函数 printf 的定义了。
之前,我们在一个C语言程序的编译和运行中,我们曾经讲过链接是把经过编译后的各个文件的内容关联起来,在这里我们要认识到链接不只是.c文件,还有库文件的参与,而这也就是链接的作用——将自定义函数对不同的.c文件进行连接,对库函数与库进行链接。
(2)动态链接和静态链接
就像我们去图书馆,图书馆里有各种各样的书可以供你阅读。比如说,你在图书馆里发现了一本好书,但是没有经过图书馆管理员的借阅允许,你就只能在图书馆里去看这本书。
如果你不想每次都跑到图书馆去看书,那么你就可以自己买一本同样的书,就可以带着这本书到任何地方去看。
同样对于动态链接,我们需要某一个函数的定义就可以直接到相应的库里去找,就不需要把库中的函数实现拷贝一份到程序文件中,减少了程序占据的存储空间大小。但是每次遇到某个函数的定义都要再去对应位置寻找,降低了程序的运行效率。
而对于静态链接,我们就需要把库中的函数实现拷贝一份到程序文件中,我们也不需要去对应的库中寻找函数定义,增加了程序的运行效率,但是又大大增加了程序占据的存储空间大小。
所以我们很多时候会发现我们下载安装的应用程序会越来越大,除了我们自己使用留下的数据,还有一部分就是软件在你不知情的情况下将一些通过动态连接的代码下载到了本地,导致应用程序占用的存储空间越来越大。
(3)静态库和动态库
静态库是指使用静态链接时,库中文件的代码全部加入到可执行文件中,这个被加载的库就是静态库。静态编译的程序在运行时不再需要库文件的查找。在Linux下静态库的后缀名一般为.a,Windows下为.lib
动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时 链接文件加载库,这个需要被查找的库就是动态库。在Linux下动态库一般后缀名为“.so”,Windows下为.dll。
前面的 libc.so.6 就是个动态库。而且 gcc 在编译时默认使用动态链接生成可执行文件
输入gcc test.o –o test,然后输入file test查看程序的连接属性
Linux上(Windows上也如此)gcc默认生成的可执行程序,是动态链接的(dynamically Linked)。
(4)静态链接形成可执行程序
如果我们就想让test.c静态链接形成可执行程序可通过下面的语句实现:
格式:gcc 源文件 -o 可执行程序名 -static
- 一般系统会自动携带C语言动态库,C语言静态库可能没有安装,如果没有的话就需要自己安装,安装输入sudo yum install glibc-static即可
- 编写一个test.c程序输入gcc test.c -o test_s -static
- 用file指令发现test_s是静态链接的(statically linked)
四、项目自动化构建工具Make/Makefile
这一部分理解起来不太轻松,需要细细琢磨
1.Make/Makefile的介绍
make是一条命令,makefile是一个文件,两个搭配使用就可以完成项目的自动化构建。
make/makefile主要用于管理大型项目的各种文件,我们现在写的程序最多也就五六个文件,但是一个大型工程中的源文件多达上千个。
其中makefile是用于保存协调项目内文件运行的规则的文件,make指令是用于解释makefile中指令的命令。一般来说,大多数的代码集成开发环境都有像make这样的命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。
3.Make/Makefile的简单使用
(1)Makefile文件的编写
我们写一个加法表,用main.c ,add.h, add.c三个文件实现。
//add.h
#include<stdio.h>
int Add(int x, int y);
//add.c
#include"add.h"
int Add(int x,int y)
{
return x+y;
}
//main.c
#include"add.h"
int main()
{
int i = 1;
for (i=1; i<9; i++)
{
int j = 1;
for (j=1; j<=i; j++)
{
printf("%d+%d=%d",i,j,Add(i,j));
}
}
return 0;
}
编写 makefile,最重要的是理解依赖关系和依赖方法。
依赖关系是指一个文件依赖另外一个文件,即想要得到一个文件,目录下必须先有另外一个文件
依赖方法则是指如何根据依赖文件来得到目标文件。
建立一个Makefile文件,注意文件名必须是makefile或Makefile,不能是其他名称,否则make不能识别。
这是加法表的Makefile编写:
依赖关系—— 目标文件:依赖文件
目标文件 add_map.out 依赖对应的依赖文件(也可以叫源文件) add.c和main.c,由于预处理已经完成了头文件的包含,所以不需要写上头文件。
这只是个例子,依赖文件可以有多个,也可以没有。
例:add_map.out:tadd.c main.c
依赖方法—— 源文件形成目标文件需要执行的指令(gcc test.c -o test.out)
依赖方法是 gcc 编译,依赖方法的执行指令必须以 [Tab] 键开头,特别注意不能是四个空格。
例:[TAB]+gcc test.c -o add_map.out
伪目标—— .PHONY
例:
.PHONY:clean(clean可替换)
clean:(clean替换,这里也要替换)
rm -f add_map.out
clean 不依赖任何文件,依赖方法是 rm -f 指令;其中 .PHONY 修饰 clean 表示其是一个伪目标,伪目标总是被执行。
(2)Makefile的使用
依旧以上一个Makefile运行
输入指令:make
make代表执行第一个依赖关系下的所有方法。
输入指令:make clean
可以发现前面生成的test.out可执行文件已经被清理
伪目标有总是被执行的特点,非伪目标指令表现为执行这个指令在结果上并没有改变那就不再执行这个指令,而伪目标的指令一定会被执行。
大概举个例子:
我们可以在命令上多次输入make,可以发现它并没有帮我们执行,因为add_map.out已经存在了,而且源文件也没有被修改,即使你再次生成可执行程序它最后结果上也是生成一模一样的可执行程序,所以系统觉得没必要编译多次,对应的依赖方法也就不会被加载到系统中。
但是伪目标就不行了,输入make clean,clean对应的指令只要你输入就一定会执行。清理项目的指令执行多次也没事儿,如果没有对应的文件,系统直接忽略就好了。
再次解答一下为什么clean需要写成make clean,而编译直接写make就可以了。
如果我们把Makefile中的clean移动到第一行,再去make,这次make执行的是rm -rf add_map.out这条命令。这就说明了在Makefile中,执行make命令的时候,不指定具体目标,它就会默认执行第一个目标。
4.make智能识别的原理
make是怎么知道我的源文件没有被修改从而不让我多次make,那它靠什么识别呢?
理解这个的原理,我们需要一个查看文件的属性信息的命令
格式:stat+文件名
比如我们查看main.c文件的属性
可以看到Access\Modify\Change三个时间(以时间戳储存),它们的初始时间记录都是文件被创建的时间。
- Access 指最后一次读取的时间(访问)(比如在终端上用cat、more 、less、grep、 cp 、file 一个文件时都应该更新这个时间,不过它不会每次都更新,因为访问是个非常频繁的操作,每一次我们有意无意的操作,系统都更新它的时间是没必要的)
- Modify 指最后一次修改数据的时间(修改)(意思为更改,更改的是内容,“或者“写入)
- Change 指最后一次修改元数据的时间(改变)(意思为改变,改变的是状态或属性,比如对一个文件或者目录作mv、chown、chgrp操作)
change时间会受到modify行为的影响,因为如果向文件写入内容,表示着它的大小会变化,顺带改变了它的属性。
根据了解上面的知识,其实make指令就是根据生成的可执行程序的修改时间,然后再根据源文件修改时间进行判断的。
如果可执行程序的时间比源文件的时间要更新或者说更晚,就不会进行重新生成可执行程序,从而减少不必要编译过程。
这里我们就可以理解所谓的“伪目标”,其实就是被.PHONY修饰的目标不遵循上面的时间判断规则,只要你输入了,它就一定会执行。
5.Make/Makefile实例的执行过程
在默认的方式下(只输入 make 命令),make就会在当前目录下找名字叫“Makefile”或“makefile”的文件。如果找到对应文件,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“main.c 和 add.c”作为依赖文件。
如果add_map.out的可执行程序文件不存在,或是add_map.out的可执行程序所依赖的依赖文件(本例子中是两个.c 文件)的修改时间要比目前存在的可执行程序新,这样,他才会执行后面所定义的命令来生成 add_map.out的可执行程序的目标文件,依赖文件不会被清除。
如果add_map.out的可执行程序文件所依赖的 .c 文件也不存在,那么make会在当前文件中找目标为 .o 文件的依赖性,如果找到则再根据那一个规则生成 .o 文件。(这有点像一个堆栈的过程)
我把其中一个依赖文件经过编译形成.o文件,这个.o文件也可以被识别并形成可执行程序,只要你的文件是可以和原来的依赖文件形成指令的关联的,那就make可以运行。
整个make是有依赖性的,从可执行程序开始,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。(比如说:test.c
make只会检查文件的依赖性,如果在我找了文件的依赖关系之后,冒号后面的依赖文件怎么也找不到,那就甭提别的,我不干了。
像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行(make clean),清除所有的目标文件,以便重编译。
于是在我们编程中,如果这个工程已被编译过了,当我们修改了其中一个源文件,甚至是头文件,只要文件修改时间要比已经生成的可执行程序要新,这个可执行文件就会被重新生成。
五、gdb调试工具
1.什么是gdb
GDB是一个由GNU开源组织发布的、UNIX/LINUX操作系统下的、基于命令行的、功能强大的程序调试工具。对于一名Linux下工作的c++程序员,gdb是必不可少的工具。
2.debug和release
(1)Debug称为调试版本,它在源代码的基础上不作任何优化,便于程序员调试程序。
(2)Release称为发布版本,它往往是对程序进行了各种优化,例如减小程序大小和加快运行速度等,以便用户很好地使用。
(3)这两种状态下的代码运行结果可能会不同。
只有debug版本的程序才能调试,gcc默认生成的程序是release版本的,如果想生成debug版本的可执行程序可以在编译语句后面加上-g
输入:gcc test.c -o test -g
test就是生成的debug文件
3.gdb调试代码
以调试test为例,简单说明最基本的几个命令,上面是VS上的调试方法,下面是gdb的操作方法。源码如下:
#include<stdio.h>
int main()
{
int i = 1;
for (i=1; i<10; i++)
{
int j = 1;
for (j=1; j<10; j++)
{
printf("%d×%d=%d",i,j,i*j);
}
printf("\n");
}
return 0;
}
(1)进入调试
在VS中进入调试可以按F5、F10、F11都可以进入调试
格式:gdb+可执行程序文件
输入:gdb test 进入调试,进入gdb就可以输入指令了
(2)显示源代码
格式:list/l
list 展开源代码的前十行,如果还想看到后面的内容可以点击Enter十行十行展开
(3)断点的添加、删除和信息显示
在visual studio中我们用F9添加断点,设置断点的行前面会有一个红色圆圈
格式:break+代码行数/b+代码行数
输入:b 7 相当于在第四行添加断点
在visual studio添加断点后可以右键点击红点设置条件,在符合该条件的情况下才停止运行
格式:break+代码行数+if+条件/b+代码行数+if+条件
输入:b 6 if i==5 就在第六行加入了一个条件为i==5的断点,运行后刚好停在相应位置。
在visual studio中我们用设置断点的行前面的红色圆圈指示断点的位置,右击查看条件信息
格式:info break/info b
输入:info b 查看断点
在visual studio中删除断点在相应行按F9即可
格式:delete+断点编号/del+断点编号
输入:del 1 就删除了一号断点
(4)run运行调试程序与continue继续运行
在visual studio中我们用F5运行到断点处,而ctrl+F5是开始执行不调试,相当于不打开gdb直接运行debug程序。
格式:run/r
输入:r 开始运行到第七行 int j = 1;
但是run是指运行一个程序,如果到达了断点还想要继续向下运行就需要continue语句。
格式:continue/c
输入:c 继续运行至断点或者程序结束
(5)逐语句和逐过程调试
next为gdb的单步调试,当遇到函数调用时,不进入函数体,相当于VS的F10
格式:next/n
next为gdb的逐语句调试,当遇到函数调用时,不进入函数体,相当于VS的F10
格式:step/s
step为gdb的逐过程调试,当遇到函数调用时,进入函数体,相当于VS的F11
不管是逐过程还是逐语句,它们都需要在输入run指令让程序运行起来后才可以使用。
(6)查看某个变量的值
在调试过程中,我们需要查看当前某个变量值的时候,可使用print 命令显示该值,但是只能显示一次下次不会再显示。
格式:print var(var表示任何一个变量或表达式)
在调试过程中,我们需要查看当前某个变量值的时候,可使用 print 命令显示该值,但是只能显示一次下次不会再显示。
格式:display var(var表示任何一个变量或表达式)
使用 display 命令就可以一直显示该值。
(7)退出gdb
VS中退出调试可以输入Shift+F5
格式:quit/q
4.其他命令
until:当你厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体
until+行号: 运行至某行,不仅仅用来跳出循环
finish: 运行程序,直到当前函数完成返回,并打印函数返回时的堆栈地址和返回值及参数值等信息
call 函数(参数):调用程序中可见的函数,并传递“参数”,如:call gdb_test(55)
disable 断点号n:暂停第n个断点
enable 断点号n:开启第n个断点
clear 行号n:清除第n行的断点
还有很多其他的命令,可以自己去试一试。
六、进度条程序
1.前置知识
(1)\r&&\n
回车概念(\r)——回车是把光标移到一行的开始
换行概念(\n)——换行是跳到下一行的开始
(2)行缓冲区
我们定义一个test.c文件,代码如下:
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("%hello world\n");
sleep(3);
return 0;
}
结果打印了hello world也换行了,然后才睡眠三秒
我们稍加改变
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("%hello world\r");
sleep(3);
return 0;
}
我们发现程序等待了一会儿,什么也没打印出来,那么hello world的内容去哪里了呢?
这里就出现了缓冲区的概念,其实在之前的C程序中也说过缓冲区,只是没有利用过计算机的这个特点。这个hello world被放在了缓冲区中没有被拿出来放到屏幕上,我们也就什么都看不到。
再添加一个刷新缓冲区的函数
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("%hello world\r");
fflush();
sleep(3);
return 0;
}
程序就直接把数据打印出来再睡眠了。
控制台是一个输出设备,我们可以看到有一个光标随着输出去动下。数据先输出到缓冲区内,然后通过fflush清空缓冲区,输出最后带上一个\r让光标回到开头,同时由于缓冲区被清空,新的数据又被加载进缓冲区。然后再次清空缓冲区输出到屏幕,这时的输出相当于还在本行开头输出,将原来的内容再次输出到屏幕就相当于覆盖掉原来输出的部分内容。就这样不断地输出覆盖,形成一个动态的进度条。
(3)各文件内容
Makefile
progress_bar:progress_bar.c
gcc progress_bar.c main.c -o progress_bar
.PHONY:clean
clean:
rm -f progress_bar
main.c
#include"progress_bar.h"
int main()
{
proc();
return 0;
}
progress_bar.c
#include"progress_bar.h"
void proc()
{
int i = 0;
char arr[101];
const char* p = "|/-\\";//定义一个数组,让它每次从头到尾打印数组的元素就可以做到旋转的效果
memset(arr,'\0',sizeof(arr));//把所有内容都设置为\0
while(i<=100)
{
printf("[%-100s][%d%%][%c]\r",arr,i,p[i%4]);//注意控制格式
fflush(stdout);//刷新缓冲区
arr[i++] = STYLE;//每次把一个\0替换为STYLE对应的符号
usleep(20000);//睡眠一段时间,usleep的睡眠时间以毫秒为单位
}
printf("\n");
}
progress_bar.h
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#define STYLE '#'
extern void proc();
成果如下:
七、git上传代码
1.git的介绍
git是一个版本控制器,当我们开发一个项目时,需要写很多很多的文件。这些文件管理起来很麻烦,所以我们可以上传到某一个网站上他来帮我们管理,我们可以上传也可以取。同时,如果我们更新了一个或者几个文件,这个网站会帮我们比对上一个版本和这个新版本的区别并更新到这个版本,原先的版本也是可以看到和取到的。这样的版本管理网站就是版本控制器,git就是其中之一。
2.git的历史
直到 2005 年 4 月,我们的Linux之父雷纳兹·托瓦兹在开发完Linux后,世界各地的志愿者开发团队都在为Linux更新代码以提高其运行效率和稳定性,托瓦兹自己管理着庞大的 Linux 内核源码,这是一个相当复杂的工作。
于是托瓦兹就与当时的付费的版本控制器BitKeeper(BK)协商,对方也同意了Linux的代码管理可以免费使用他们公司的版本控制器。但是,有一些Linux社区的开发人员试图去破解BitKeeper,这引发了BK 的创始人Larry McVoy的不满,取消了免费使用的授权。
此时托瓦兹又需要自己去管理源码了。最后托瓦兹和Linux社区内的大佬们模仿BitKeeper自己开发了一个免费的版本管理器并公诸于世,这个写成的版本管理器就是Git。而Git经过商业化改造就形成了现在国外的Github和国内的Gitee
3.git的使用
(1)安装
我们的Linux系统中可能没有预装git,需要安装,我的已经安好了。
yum install -y git
(2)gitee上传代码的准备工作
由于在国内访问Github链接十分不稳定,我们更多使用的还是Gitee
在注册登陆后界面是这样的(图片是在网上找的):
点击右上角的加号,然后点击新建仓库,按照下面的操作执行就好
初始化仓库等按照下面的按就可以了
这样我们就创建好了一个仓库,我给它取名叫Linux
点击右上角的管理,到最下面设置仓库为公开并选择对应的许可证就可以了
(3)下载项目到本地
将云端的数据拷贝到当前机器上
格式:git clone+仓库的https链接
这个https链接在这里:
输入指令后,先输入用户名,必须是英文的,再输入密码就完成拷贝了。
这个Linux文件夹就是我们和gitee仓库的连接中介,我们将文件放到文件夹里,再通过git三板斧传到gitee网站的仓库里,此时代码的上传就完成了。
(3)gitee三板斧——add、push和commit
先把代码放到仓库对应的的目录下
三板斧之一:add
格式:git add+文件
三板斧之二:commit
格式:git commit -m "内容"(这个内容相当于你对本次提交的描述,虽然写什么都行,但对我们而言不能乱写)
三板斧之三:push
格式:git push
通过三板斧,我们就把文件传输到gitee上了,在仓库中可以看到。
其实git的操作不止这三板斧,还有很多有趣的东西值得学习。
最后,感谢你能看到这里,这些工具的使用虽然不难,但是知识很细,光靠别人讲是会不了的,需要自己去练习体会。
版权归原作者 聪明的骑士 所有, 如有侵权,请联系我们删除。