【Linux根本】基本开辟工具的使用

莱莱  金牌会员 | 2024-12-15 23:28:17 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 837|帖子 837|积分 2511

目次
一、编译器——gcc/g++的使用
gcc/g++的安装
gcc的安装:
g++的安装:
gcc/g++的基本使用
gcc的使用
g++的使用
动态链接与静态链接
步调的翻译过程
1. 一个C/C++步调的构建过程,步调从源代码到可实行文件必须履历四个阶段
2. 理解选项的寄义
第一步预处置惩罚:
第二步编译: 
第三步汇编: 
第四步链接: 
动静态链接的认识
验证我们所写的步调是动态链接还是静态链接
file 可实行文件
ldd 指令
静态库的安装
C语言安装静态库:
C++安装静态库:
静态链接
二、项目自动化构建工具——make/Makefile
make与Makefile的概念
Makefile的原理
Makefile的语法
依赖关系和依赖方法
项目清算
如何理解 .PHONY 修饰的是一个伪目的
我们可以知道被.PHONY修饰的对象总可以被实行
gcc 如何得知 mytest 是最新呢 
如何证实 gcc 根据修改的时间得知 mytest 是最新
Makefile的推导规则
三、版本控制器——git
git 是什么
git 的操作
1. 进入gitte官网,先新建一个堆栈
2、Linux中提交代码操作
1. git clone拉取远端堆栈克隆到当地(目次克隆到当地)
2. git add . 把需要提交的目次(项目)复制或剪切到当地堆栈
3. git commit -m  ‘提交日记’  修改之后的代码添加进去提交当地库
4. git push 推送到远程堆栈,这才是把当地堆栈的代码推送至远程堆栈
5. 如果在远端我们的代码修改了,git status 检察 git 的状态 
6. git log 检察提交历史记载
四、调试器——gdb
调试之前我们先创建文件以及准备调试代码
test.c 文件
编译成debug版本
gdb 的使用
gdb的安装
​编辑
基本使用
① l 检察代码
② b 打断点、info检察断点、d 删除断点
③ r 开始调试,调试起来
④ n 逐过程、s 逐语句
⑤ c 运行到下一个断点处
⑥ bt 检察调用堆栈
⑦ finish 运行完当前函数
⑧ p 检察变量值
⑨ display 常显示变量的值、undisplay取消常显示
⑩ until 跳出循环
⑪ info locals 检察特定区域的暂时变量
五、Linux小步调——进度条的实现
基本概念
fflush的使用
回车换行的理解
倒计时步调的实现
进度条的实现
process.h
process.c
main.c
Makefile

一、编译器——gcc/g++的使用

gcc/g++的安装

gcc的安装:

  1. yum install -y gcc
复制代码
g++的安装:

  1. yum install -y gcc-c++ libstdc++-devel
复制代码
gcc/g++的基本使用

   我们在使用 gcc/g++ 进行编译的时候一般都带选项: -o
  在 gcc -o test test.c 这个命令中,gcc编译器会对test.c这个源文件进行编译和链接操作。正常情况下,如果没有-o选项,gcc会天生一个默认名称的可实行文件(在 Linux 等系统下通常是a.out)。
  但是当使用-o test时,编译器会将天生的可实行文件命名为test,而不是默认的a.out。
  
  注意:gcc -o test test.c 和 gcc test.c -o test 这两种写法都是正确的。在这两种情况下,gcc编译器都可以或许理解-o test是要将编译天生的可实行文件命名为test,而test.c是要编译的源文件。
  gcc的使用


g++的使用


动态链接与静态链接

   我们在学习动态链接与静态链接的时候需要对步调的翻译过程进行复习一下,这都是我们C语言阶段就学习过的内容。
  步调的翻译过程

1. 一个C/C++步调的构建过程,步调从源代码到可实行文件必须履历四个阶段



  • 预处置惩罚:头文件的睁开、宏替换、条件编译、去掉解释     
  • 编译:检查语法错误,将预处置惩罚后的源代码天生为汇编代码 
  • 汇编:汇编代码转换成二进制的呆板码   
  • 链接:多个目的文件的文件链接成一个可实行文件
2. 理解选项的寄义

   当天生了一个可实行文件,但是我们并不想让他直接编译完成,编译的过程是一下就走完了,而接下来我们想看他的过程,就在编译的时候带上选项就可以检察每一个阶段的过程。
  
  注意:C++同样是这种过程。
  在测试之前我们重新在 test.c 中编写一个用于测试的代码 
  1. #include <stdio.h>   
  2. #define N 20   
  3. int main()     
  4. {   
  5.     //测试去掉注释   
  6.     printf("hello wolrd 1\n");   
  7.     //printf("hello wolrd 2\n");   
  8.     //printf("hello wolrd 3\n");   
  9.     //printf("hello wolrd 4\n");   
  10.     //printf("hello wolrd 5\n");   
  11.     //printf("hello wolrd 6\n");   
  12.     //printf("hello wolrd 7\n");   
  13.     //printf("hello wolrd 8\n");   
  14.     //printf("hello wolrd 9\n");   
  15.     //printf("hello wolrd 10\n");   
  16.    
  17.    //测试条件编译   
  18. #ifdef CONDITION   
  19.     printf("condition\n");   
  20. #else   
  21.     printf("no condition\n");   
  22. #endif      
  23.    
  24.     //测试宏替换   
  25.     printf("宏:%d\n", N);   
  26.     return 0;   
  27. }
复制代码
第一步预处置惩罚:

   选项 -E 从现在开始,进行步调的翻译,当你将预处置惩罚做完,就停下来。      
  如果去掉 –o 预处置惩罚这么多内容打印到显示器上特别欠好看,选项 -o: 指明形成的暂时文件名称 .i, 使用 .i 作为预处置惩罚文件的扩展名是一种比较常见的约定,就像c语言文件名后缀是 .c一样。
  
  观察下面的代码,我们可以看到多了800多行代码,这就是头文件的睁开,把库中的代码拷贝过来了。
  然后就进行了宏替换,预处置惩罚阶段就已经做了的。
  末了只剩hello world 1,这就是去掉解释。
  条件编译这里把这个没有的也裁掉了,这就是条件编译。
  
  注意:我们要用的头文件、必须在C语言库中存在,平时在某个平台写代码的时候,头文件必须在该系统中存在 , Linux中库头文件在 /usr/include/stdio.h 目次下,平时我们安装VS2022,同时也会把头文件库文件安装到v2022所对应的目次下,这也就是我们为什么写代码可以自动补齐的原因
  1. gcc -E test.c -o test.i
复制代码

我们不界说CONDITION也可以通过传参来界说 

第二步编译: 

   编译:把c语言代码变成汇编代码。
  选项 -S:从现在开始,进行步调的翻译,做完编译工作,变成汇编之后,就停下来。
  编译之后Linux下通常以 .s后缀末端作为存储汇编代码的文件
  1. gcc -S test.i -o test.s
复制代码

第三步汇编: 

   汇编语言不能被盘算机直接实行,把汇编变成二进制目的文件
  选项 -c:将汇编代码转成二进制目的文件就停下来,没有进行链接。
  
  vim  test.o 进入该文件我们看不懂,但是可以 od test.o进行阅读该二进制文件。
  test.o固然是二进制文件,但是不能被实行,我们可以加上权限,发现还是不能被实行,原因在于少了一步链接,这三个步骤只在编译你写的代码,好比你在c语言上的printf的函数是你写吗,你只是调用了printf,printf的实现你没有写。
  1. gcc -c test.s -o test.o
复制代码

我们实行该目的文件发现不能被实行,原因是因为缺少一步链接

第四步链接: 

   我们把前面三个步骤完成之后就可以进行链接操作了,链接是将多个目的文件以及所需的库文件整合在一起的关键过程。
  把你写的代码和 C标准库中的代码合起来:这就是链接的过程,可以不带任何选项
  gcc test.o –o mytest 
  链接就形成了可实行步调,形成可实行的二进制步调(库+你的代码)。
  1. gcc test.o -o mytest
复制代码


动静态链接的认识

   我们要明确本身编写的代码与库中的代码是差别的。像 C 标准库,是他人事先准备好以供我们直接使用的。好比我们在代码里写了像printf这类库函数的调用语句,实际上我们只是进行了调用,并没有去实现这些函数。只有在进行链接这一环节时,库函数对应的详细实现才会和我们写的代码关联起来。
   而链接的本质,核心就是解决我们调用库函数时,如何与标准库(或其他干系库)建立关联的问题,这其中就涉及到选择动态链接还是静态链接的方式。动态链接是在步调运行时按需加载共享库来获取函数实现,而静态链接则是在链接阶段直接把库函数的代码复制到可实行文件当中。
  
  

  • 动态链接

    • 优点

      • 资源节省:可实行步调体积小,因为不包含库代码,运行时才加载共享库,节省内存、磁盘和网络资源。比方多个步调共享动态库内存实例。
      • 更新方便:库更新时,只要接口兼容,步调无需重新编译就能使用新功能。

    • 缺点

      • 运行依赖:依赖外部情况,缺少库或版本不匹配时步调无法运行。
      • 性能损耗:启动要加载库,运行中调用库函数有系统开销,影响性能和启动速度。


  • 静态链接

    • 优点

      • 独立稳定:不依赖外部库,库升级、删除或移动不影响步调运行,步调有很好的稳定性。
      • 举动可预测:运行举动由自身代码决定,便于在差别情况部署和调试。

    • 缺点

      • 资源占用:可实行步调体积大,占用更多磁盘和内存空间,多个步调用雷同静态库会有代码冗余。
      • 更新不便:库更新时,步调要重新编译才气使用新功能,大型项目中耗时耗资源。


  动静态库可以类比成拍照爱好者与照片滤镜库
假设你是一个拍照爱好者,你有一部相机(相称于步调本身),你平时会拍摄各种各样的照片(步调实行各种任务)。
动态库的情况
现在有一个非常流行的在线照片滤镜应用(相称于动态库)。当你拍完照片后,你想要给照片添加一些特别的滤镜效果,但是你的相机本身没有这些滤镜功能(就像步调本身没有这个功能的实现)。
你打开相机中的一个连接功能(相称于步调中的函数调用接口),连接到这个在线滤镜应用。每次你想要使用一个滤镜的时候,相机就会通过网络(相称于运行时情况)向这个在线滤镜应用发送照片和滤镜哀求(就像步调运行时哀求动态库中的功能,这就是动态链接)。这个在线滤镜应用会处置惩罚你的照片,添加滤镜后再把处置惩罚后的照片发送回你的相机(就像动态库提供功能实现并返回效果)。
很多拍照爱好者都可以使用这个在线滤镜应用(多个步调共享动态库)。而且,如果这个滤镜应用更新了新的滤镜效果大概优化了滤镜算法(动态库更新),只要相机和滤镜应用之间的连接方式(接口)稳定,你的相机依然可以使用这些新功能来处置惩罚照片。
静态库的情况
厥后,你发现每次连接到在线滤镜应用很麻烦,而且偶然候网络欠好就没办法使用滤镜。于是你购买了一个带有大量滤镜功能的存储卡(相称于静态库),这个存储卡可以插入你的相机。
当你把存储卡插入相机后,相机就把存储卡里的滤镜功能(就像静态库中的代码)复制到了相机内部的可用功能列表中(就像静态链接把库代码复制到可实行文件,这就是静态链接)。现在你拍摄完照片后,想要添加滤镜,相机就可以直接使用存储卡里已经复制过来的滤镜功能来处置惩罚照片,不需要再连接到外部的在线滤镜应用(不依赖外部库运行)。即使这个在线滤镜应用因为某些原因停止服务(好比公司倒闭等情况,就像动态库不可用),你依然可以在相机里使用这些滤镜功能来处置惩罚照片。
验证我们所写的步调是动态链接还是静态链接

file 可实行文件


ldd 指令

   ldd  可实行文件 用于检察步调(可实行文件或共享库)依赖的动态共享库的工具。当你运行ldd命令并跟上一个可实行文件的名称时,它会显示该可实行文件在运行时需要加载的动态库的路径和名称等干系信息。
  

静态库的安装

   默认情况下都是动态链接的,如果我们想要静态链接需要怎么办呢?
  ls  /lib64/libc.a   不肯定存在静态库,静态库不存在需要本身安装。
  C语言安装静态库:

  1. yum install -y glibc-static
复制代码
C++安装静态库:

  1. yum install -y libstdc++-static
复制代码
静态链接

   在系统中,C 动态库(也称为共享库)是被多个使用该库的步调所共享的。只管使用 C 动态库的步调众多,但通常系统中只存在一份该动态库的物理副本。这体现了动态库的共享特性,即多个步调在运行时可以同时访问并使用这同一个动态库,制止了库代码在内存中的重复存储,从而节省了系统的内存资源。比方,标准 C 库(如libc.so)被众多的 C 语言步调所依赖,在系统运行时,这些步调通过动态链接的方式引用libc.so,而无需各自拥有一份独立的libc.so代码副本。
  
  静态链接并不是拷贝动态库内部的代码。静态链接是在步调编译阶段,将所需的库代码(通常是静态库,以.a末端)直接复制到可实行文件中,使其成为可实行文件的一部分,从而在运行时不再依赖外部的库(包括动态库)。所以,静态链接与动态库的机制是相互独立的,静态链接不会直接操作动态库的代码。
  
  Windows动静态链接的原理是一样的,动态库通常以.dll为后缀,静态库一般是以.lib为后缀
  

 我们也可以用C++进行静态链接


二、项目自动化构建工具——make/Makefile

make与Makefile的概念

   make 是一个命令,Makefile是一个文件。
  
  make与Makefile存在的意义:
  Makefile 所带来的好处集中体现在自动化编译方面。这并不意味着完全无需人工干预,而是在初始阶段,开辟职员需要根据项目的结构和需求编写 Makefile 文件,经心规划好源文件与目的文件的依赖关系以及相应的编译规则。一旦 Makefile 文件编写完成,后续使用 Make 命令时,它会自动读取 Makefile 中的内容,按照既定的规则和依赖关系,有条不紊地对步调或项目进行编译,无需开辟职员再次手动调解编译过程。这使得在项目开辟过程中,无论是对单个源文件进行修改后重新编译,还是对整个项目进行全新构建,都可以或许通过简单地实行 Make 命令快速、准确地完成,从而实现了高效、稳定的自动化构建过程,为软件开辟的迭代和维护提供了有力支持。
  
  注意: Makefile的 m 可以大写也可以小写。
  我们可以来使用一下Makefile,下面会阐明Makefile
就是不用再手写gcc了,直接make就可以了,看起有点弱,实在逐步就会发现这个Makefile很强

Makefile的原理

   Makefile 必须包含依赖关系和依赖方法这两个关键要素,其存在意义在于为构建项目提供自动化支持,使项目可以或许依据文件间的依赖关系和天生方法,有条不紊地完成编译、链接等构建流程,制止手动操作的繁琐与易错性,高效达成项目构建这一目的。
  
  Makefile 的运行就如同生活中的一个场景。好比,当你打电话说 “爸,我是你儿子”,此时这仅仅建立了一种依赖关系,即你依赖于本身的父亲,但仅凭这一句,父亲并不清楚你到底想要做什么,事变天然无法推进,这是因为缺少依赖方法。再好比,你不大概去找室友的爸爸借钱,原因是你和室友的爸爸之间不存在任何依赖关系。只有当你既明确了依赖关系,像打电话跟本身的爸爸表明身份,又清楚地阐述依赖方法,比方接着说 “我没钱用饭了,给我打点钱吧”,这样依赖于父亲打钱这件事变才可以或许完成。在 Makefile 中也是如此,只有同时具备依赖关系和依赖方法这两个关键要素,项目构建这件事变才可以或许顺遂实现。
  

Makefile的语法

依赖关系和依赖方法

   第一行团体我们称为:依赖关系。
mytest : test.c
目的文件(可实行步调/暂时文件) :  依赖文件列表, 这里只有一个依赖文件
这里表现 mytest  依赖于test.c
  
  第二行团体我们称为:依赖方法
  

写完了Makefile,我们make一下就会天生可实行文件,然后运行该文件就可以了

项目清算

   随着项目的不停编译和构建,会产生许多可实行步调以及一些诸如目的文件(.o文件)、中心编译文件等暂时文件。偶然候,我们大概想要重新编译整个项目,大概不想保存这些已天生的可实行步调和暂时文件了(好比它们占用了过多磁盘空间,大概之前的编译效果出现了一些未知问题想要重新开始等情况),这时候就需要一种方法来清算它们。
  
  如何清算:
  vim Makefile 进入Makefile之后接着往下输入
  clean : 这里可以为空(依赖关系可以为空,也就说clean不依赖与任何文件)
  在下来 tab 开头的就是依赖方法
  我们再用 .PHONY 修改 clean
  红色为伪目的 .PHONY 修饰的是一个伪目的
  

为什么make 和 make mytest 也是可以,而 clean 要 make clean
   第一个遇到的可以省略名称,仅此而已,默认情况下只形成一个目的文件(可实行目的文件),不指名默认就是第一个。
  

如何理解 .PHONY 修饰的是一个伪目的

   当我们不停 make 的时候,会发现不让你编译了,因为是最新的,没有修改没必要重新编译
  但是当我们再不停调用 make clean 的时候总是能实行 rm –f  mytest
  

我们可以知道被.PHONY修饰的对象总可以被实行

   但是我们一般不会这样做,将我们天生的可实行步调用.PHONY修饰, 但是这都不是紧张的,
  最紧张的是 gcc 如何得知 mytest 是最新呢???
  

我们再改回原来的Makefile

gcc 如何得知 mytest 是最新呢 

   肯定跟时间有关系,在我们学习指令的时候,就接触过一个指令 stat,stat命令是一个用于检察文件或文件系统状态信息的工具。
  
  Access(访问时间):这是文件末了一次被访问(如检察内容)的时间。
  Modify(修改时间):记载文件内容末了一次修改的时间。
  Change(改变时间):是文件元数据(属性,如权限、所有者等)末了一次改变的时间。
  

为什么change会随着modify的改变而改变呢???
   当我修改了文件内容,modify修改了,change也会随着修改,因为文件的巨细变化了,也意味着文件的属性也跟着变化,所以change会改变。
  

Change 和 Modify 的时间变化可以或许理解,但是为什么Access是指我们cat访问/vim检察该文件的时间,为什么没有任何的变化???
   访问文件不肯定改内容,改内容肯定访问,如果每次访问都修改时间,导致Access修改的频率太多了,就需要在磁盘进行多次的 IO(输入 / 输出)操作,别人设置文件让别人阅读,就不在过于关心访问时间,如果别人修改了文件时间立马更新Modify时间,所以操作系统接纳的是你访问次数到达多少次就更新一次Access的时间,这种计谋,没有什么规律,我们主要关注 Change 和 Modify。
  

我们现在知道了这么多时间,但是和 gcc 有什么关系??? 它怎么知道我们文件是最新的??? 
   先有源文件 test.c,再有可实行步调 mytest
  test.c Modify 的时间 肯定比 mytest Modify 的时间要更早
如果 test.c 内里有bug改了一下内里的内容,然后就会更新源文件test.c 的 Modify的时间,这个时候,test.c 内里的时间就是新的,比 mytest 的时间新了,就是比较两个时间的新旧。
  如何证实 gcc 根据修改的时间得知 mytest 是最新

   我们知道一个指令touch,除了创建文件还可以修改时间
  touch 可以更新文件的时间


综上所述:凡是用.PHONY修饰的就是不要拿时间尴尬刁难比了,你每次都实行一下,不要遵守时间对比的规则,每次都要实行依赖方法,而没有使用 .PHONY修饰的就是对比源文件的时间和可实行的时间变化来确定是不是需要进行编译。
Makefile的推导规则

   步调的翻译过程从预处置惩罚、编译、汇编、链接
文件从 test.i  -> test.s  -> test.o
我们的可实行文件 mytest 依赖于 test.o
而 test.o 依赖于 test.s
而 test.s 依赖于test.i
  
  我们在日常写Makefile中我们直接一步到位,直接写依赖于源文件就可以了。
  



三、版本控制器——git

   本日,就让我们一起来学习一下个人使用 git 提交代码的基本操作流程吧,后续更复杂的冲突处置惩罚、分支协作等内容我们可以再逐步深入学习。
  git 是什么

   git 就像是一个超级智能的代码 “韶光机”。假设你在写代码,就似乎在创作一幅复杂的画作。每次你对代码进行修改,无论是添加新功能、修复小错误还是优化性能,git 都能帮你记载下这个 “画作”(代码)的差别状态。
   好比说你写一个 C++ 步调,最开始只有一个简单的打印 “Hello, World!” 的功能,这是你代码的最初版本,git 就像一个相册,把这个初始版本保存了下来。之后你添加了一个盘算两个数字相加的函数,git 又会把这个有新功能的版本记载下来,就似乎在相册里新增了一张带有新内容的照片,你还可以找到原来记载下的版本(可以回顾差别的版本)。
  git 的操作

1. 进入gitte官网,先新建一个堆栈

    gitte官网:Gitee - 基于 Git 的代码托管和研发协作平台
   进入官网之后我们先创建一个堆栈
  


初始化堆栈,设置堆栈模板,选择分支,我们先这样进行创建,后续需要深入学习git

远端就会形成一个这个堆栈 

2、Linux中提交代码操作

git 的安装:
  1. sudo yum install -y git
复制代码
准备我们要提交的代码文件


1. git clone拉取远端堆栈克隆到当地(目次克隆到当地)

   我们暂时先使用HTPS,因为比较简单如果用SSH,还需要进行设置
  先复制好,然后进入Linux指令行中输入 git clone 粘贴刚刚复制的内容
  

git clone:把远端堆栈克隆到当地
   当我们克隆到当地的时候,偶然候大概需要输入本身gitee的账号和密码
  

Linux中进入该堆栈,而远端隐蔽了.git文件


注意点: 当我们初次安装好了git之后系统会提示你进行设置 用户名 和 邮箱
要将码云(Gitee)的堆栈与 Linux 系统关联起来
设置 Git 用户名和邮箱(如果还没设置的话)---我们在提交的过程中会出现提示
  1. git config --global user.name "你的gitte用户名"
  2. git config --global user.email "你的邮箱"
复制代码
2. git add把需要提交的目次(项目)复制或剪切到当地堆栈

   git add相称于添加到暂时堆栈(还没提交到当地库),相称于一个 "准备区"
  


3. git commit -m  ‘提交日记’  修改之后的代码添加进去提交当地库

   git commit -m  ‘提交日记信息’ 这个日记必须写,也不要乱写。
这才是提交到了当地堆栈 但是远端堆栈还没没有提交,所以我们需要推送到远端堆栈
  

4. git push 推送到远程堆栈,这才是把当地堆栈的代码推送至远程堆栈


我们检察远端堆栈发现多了一个我们提交的文件

5. 如果在远端我们的代码修改了,git status 检察 git 的状态 

   如果远程库和当地库没有同步的话,我们就 git pull 先把远端的拉到当地,同步一下,然后再进行 push
  pull 的本质就是强制我们每一个人必须和远端堆栈保持一致,不一致了就pull一下。
  


git pull 同步远程堆栈,当地库与远程堆栈保持一致


如果你当地要删除这个文件都在前面加上 git 

6. git log 检察提交历史记载



四、调试器——gdb

   

  • 在调试思绪上,无论是在 Windows 还是 Linux 系统下,目的都是为了找出代码中的错误。都需要关注变量的初始化、赋值、变化过程,检查函数的参数传递是否正确、函数内部逻辑是否正确,以及循环结构是否按照预期实行(如循环次数是否正确、循环体中的逻辑是否正确等)。
  • 好比,在检查一个盘算数组元素总和的函数时,都需要考虑是否正确地遍历了数组,是否正确地累加了元素的值,思绪都是从步调的输入、中心处置惩罚过程到输出,逐步验证代码的正确性。
  调试之前我们先创建文件以及准备调试代码

test.c 文件

  1. #include <stdio.h>
  2. // 函数用于统计给定范围内能被divisor整除的数的个数及它们的和
  3. void countAndSum(int start, int end, int divisor, int *count, int *sum) {
  4.     *count = 0;
  5.     *sum = 0;
  6.     int i = 0;
  7.     for (i = start; i < end; i++) {  // 这里循环条件有错误,少包含了边界值end,应该是i <= end,导致可能少统计一些数
  8.         if (i % divisor == 0) {
  9.             (*count)++;
  10.             *sum += i;
  11.         }
  12.     }
  13. }
  14. int main() {   
  15.     int start = 1;   
  16.     int end = 18;
  17.     int divisor = 3;   
  18.     int count;   
  19.     int sum;                                                                                                                       
  20.    
  21.     countAndSum(start, end, divisor, &count, &sum);   
  22.     printf("在 %d 到 %d 范围内,能被 %d 整除的数有 %d 个,它们的和是 %d\n", start, end, divisor, count, sum);   
  23.     return 0;   
  24. }   
复制代码

编译成debug版本

   在学习C语言的时候我们就知道编译存在两种版本:一种是release发布版,目的:主要用于向用户发布最终产物,另一种是debug调试版,目的:专为开辟职员在开辟和调试阶段使用。
  
  要用 gdb 调试,变成debug版本才可以调试,起首要进行给编译条件添加 -g,表现天生debug 版本
  


gdb 的使用

我们要调试,直接 gdb 可实行步调,发现我们还没有gdb,所以需要安装

gdb的安装

  1. sudo yum install -y  gdb
复制代码


基本使用

① l 检察代码

   l 的全称是 list
  l 0 从0行显示,想再往后显示不需要输指令了,直接回车可以继承往下显示代码。
  

② b 打断点、info检察断点、d 删除断点

   打断点:b  全称 breakPoint  b 20    b 23都是打断点
  检察断点: info b
  去掉断点:d  全称 deletePoint     d 24这样删除不了断点,删除是删除断点编号,添加断点之后就有了断点编号。
  

③ r 开始调试,调试起来

   步调跑起来,在断点处停下来,r  全称run
  r 相称于 Visual Studio下的 F5开始调试,VS 下 F5开始调试:有断点就在断点处停下来,没有断点就直接跑完了,到另外一个断点还是F5。
  

④ n 逐过程、s 逐语句

   启动调试之后下一步就可以逐过程 F10,这里是 n  全称next 一行一行往下走
  想进入这个函数逐语句 F11,这里是 s  全称 step,可以进入某个函数
  


⑤ c 运行到下一个断点处

   c  全称continue运行到下一个断点  可以一直c运行到下一个断点处
  

⑥ bt 检察调用堆栈

   s进去一个函数后, bt 检察函数的调用堆栈,出了函数后还可以继承检察调用堆栈
  

⑦ finish 运行完当前函数

   通过这个finish可以确定这个函数是否存在错误
  

⑧ p 检察变量值

   p 只能暂时检察,我们要边看边调试变量的值。
  

⑨ display 常显示变量的值、undisplay取消常显示

   常显示可以显示变量值和地址,也可以取消变量值和地址,但是取消的时候也是取消编号。
  

取消常显示

until 跳出循环

   如果问题不在循环内里我们要跳出这个循环,直接until,然后继承 n 往下走
  

info locals 检察特定区域的暂时变量



五、Linux小步调——进度条的实现

基本概念

fflush的使用

   这个代码会先出现 hello world,然后休眠 2 秒。
  

   如果我们去掉\n就没有打印内容,两秒之后内容才打印出来,这里先实行的是printf,然后实行的是sleep,因为我们的代码是顺序结构。
  
先实行printf不便是数据先显示,printf已经实行完了,只不外数据在缓冲区内里,没有显示出来 ,缓冲区我们不知道是什么,我们目前只需要知道它是一块内存就够了,如果我们想要这个内容刷新出来,可以使用 fflush(stdout)。
  而 \n 刷新的是行缓冲。
  

fflush(stdout)刷新缓冲区 

回车换行的理解

   有一组概念回车换行,回车换行是两个概念,回车是回到当前行的开始,换行是换到下一行   \r  回车 
  \n 换行 
  \r\n 回车换行
  
  注意:语言层面 \n 就是回车换行
  倒计时步调的实现

   有了这两个根本铺垫,我们就可以完成一个简单的倒计时了。
  反斜杠n就是回车换行,我们不能使用回车换行,我们就用反斜杠 r
  

fflush(stdout)刷新缓冲区 

进度条的实现

   有了这些根本之后,我们实现这个进度条就很轻易上手了。
  进度条肯定是循环的,头文件不需要在Makefile中写,因为会在源文件中睁开。
  
  注意点:\是C语言中的特别字符,如果我们要显示 \ 就需要\\
                 模上4之后范围就在0-3
  

我们先把文件建立好,写好之后先简单的测试一下Makefile是否正确

process.h

  1. #pragma once     
  2. #include <stdio.h>   
  3. #include <unistd.h>   
  4. #include <string.h>   
  5. #define NUM 101   
  6. //#define STYLE '#'   
  7. #define S_NUM 5      
  8. //函数的声明                                                                                                              
  9. extern void ProcessOn();
复制代码
process.c

  1.   #include "process.h"   
  2.   //函数的实现
  3.   void ProcessOn()
  4.   {
  5.      int cnt = 0;
  6.      //字符数组
  7.      char bar[NUM];
  8.      //进度条样式
  9.      char STYLE[S_NUM] = {'+','#','@','*','&'};
  10.      //初始化bar                                                                                                                  
  11.      memset(bar,'\0',sizeof(bar));
  12.      //旋转光标            
  13.      const char* lable = "|/-\";
  14.      while(cnt <= 100)     
  15.      {                     
  16.          //控制格式        
  17.          printf("[%-100s][%d%%][%c]\r",bar,cnt,lable[cnt%4]);
  18.          fflush(stdout);   
  19. E>       bar[cnt++] = STYLE[N];                  
  20.          usleep(50000); //1秒 = 10000毫秒,3秒跑完
  21.      }                           
  22.      printf("\n");               
  23.   }                              
  24.         
复制代码
main.c

  1. #include "process.h"                                                                                                               
  2. int main()   
  3. {   
  4.     ProcessOn();   
  5.     return  0;   
  6. }   
复制代码
Makefile

  1. ProcessOn:main.c process.c   
  2.     gcc main.c process.c -o ProcessOn -DN=1  #-D定义宏                                                                             
  3. .PHONY:clean                                                           
  4. clean:                                                                 
  5.     rm -rf ProcessOn                                                                  
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

莱莱

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表