目录
前言
gdb
断点
打断点
查看、删除断点
断点使能
调试
显示数据
其他指令
‘/r’的使用
行缓冲区
小程序
前言
🥑在 Linux 下我们可以通过 gcc 进行编译,但与 vs 相比若想对代码进行调试,我们还需要学会使用调试器 gdb 。
🥑我们都知道程序的发布方式有两种,release 版本和 debug 版本,而 release 版本是无法进行调试的,在 VS 之中默认为 debug 版本,并且可以通过选取来直接更改程序的不同版本。
🥑但在 Linux gcc/g++ 出来的二进制程序,默认是 release 模式,因此无法调试。若想转换成 debug 版本,则必须在源代码生成二进制程序的时候, 加上 -g 选项。
gcc -g -o text text.c
🥑同时我们也注意到,debug 版本的可执行程序明显占的空间较大,这是因为 release 版本是最终用户在使用的版本,且用户根本不需要对文件进行调试,为了节约空间占比,release 版本便不会加上文件的调试信息,这也是为什么 release 版本下无法进行调试的原因。
-rwxrwxr-x 1 Alpaca Alpaca 9544 Feb 3 11:56 text.debug-rwxrwxr-x 1 Alpaca Alpaca 8384 Feb 3 11:57 text.release
gdb
🥑当我们拿到 debug 版本的可执行文件之后就可以进行调试了。直接 gdb + 可执行文件名 就可以打开调试器了。
[Alpaca@VM-12-9-centos myfile]$ gdb text.debug //gdb + 文件名GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7Copyright (C) 2013 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty" for details.This GDB was configured as "x86_64-redhat-linux-gnu".For bug reporting instructions, please see:<http://www.gnu.org/software/gdb/bugs/>...Reading symbols from /home/Alpaca/myfile/text.debug...done.(gdb) //进入后的命令行
🥑l + 行号(中间要有空格): 从该行开始向下打印10行。若代码过长再按回车会自动重复上一个指令进行操作。
(gdb) l 11 #include<stdio.h>2 #include<unistd.h>3 4 void plus(int* p)5 {6 *p *= 2;7 }8 9 int main()10 {(gdb) //再按一次回车11 int count = 0;12 while (1)13 {14 printf("%d/n", count);15 count++;16 plus(&count);17 }18 return 0;19 }(gdb)
🥑r :开始调试 ,与VS中的F5效果相同,若程序非死循环或过程中无断点则会直接执行到程序结束。
断点
打断点
🥑 d + 行号 :在该行打断点。
🥑 d + 函数名 : 在该函数有效行处打上断点。
(gdb) b 11 //以行号打断点Breakpoint 3 at 0x40054e: file text.c, line 11.(gdb) b main //找函数打断点Breakpoint 1 at 0x40054e: file text.c, line 11.
查看、删除断点
🥑 info b :查看所有断点信息。
🥑 d + 断点编号 :删除断点
(gdb) info bNum Type Disp Enb Address What3 breakpoint keep y 0x000000000040054e in main at text.c:114 breakpoint keep y 0x0000000000400555 in main at text.c:145 breakpoint keep y 0x0000000000400569 in main at text.c:15
(gdb) d 4(gdb) info bNum Type Disp Enb Address What3 breakpoint keep y 0x000000000040054e in main at text.c:115 breakpoint keep y 0x0000000000400569 in main at text.c:15
断点使能
🥑 disable : 禁用断点。(当禁用断点后再次运行直到程序结束才会停止)
🥑 enable : 使用断点。
调试
🥑 n :逐过程调试。(类似于VS中的F10,若遇到函数调用不会进入到函数之中)
🥑 s :逐语句调试。(类似于VS中的F11,会进入到函数之中继续调试)
//逐语句Breakpoint 3, main () at text.c:1111 int count = 0;(gdb) n14 printf("%d/n", count);(gdb) n0Breakpoint 5, main () at text.c:1515 count++;(gdb) n16 plus(&count);(gdb) 17 }//逐语句Breakpoint 5, main () at text.c:1515 count++;(gdb) 16 plus(&count);(gdb) plus (p=0x7fffffffe42c) at text.c:66 *p *= 2;(gdb) 7 }(gdb) main () at text.c:1717 }
显示数据
🥑 p + 变量名 :打印出该变量当前的值。
🥑 display + 变量名:跟踪查看一个变量,每次停下来都显示它的值。
🥑 undisplay + 编号 : 取消对变量的跟踪查看。
(gdb) p count$4 = 2(gdb) display count3: count = 2(gdb) undisplay 3
其他指令
🥑 c :从一个断点处直接执行到下一个断点。
🥑 set var:修改变量的值。
🥑 finish : 执行完一个函数。
🥑 until + 行号:跳至该行。
🥑 bt :查看各级函数调用及参数。
🥑 quit :退出gdb。
‘/r’的使用
🥑在Linux下以下的三条语句会产生三种不一样的结果。
printf("hello world/n"); //正常打印后暂停一秒 sleep(1); printf("hello world"); //暂停了一秒后语句和提示符一起打印出来 sleep(1); printf("hello world/r"); //无语句打印 sleep(1);
hello world[Alpaca@VM-12-9-centos myfile]$ hello world[Alpaca@VM-12-9-centos myfile]$[Alpaca@VM-12-9-centos myfile]$
行缓冲区
🥑在字符串被打印出来之前,会将其先加载到行缓冲区之中,但不是立刻就会打印出来。因此在第二条语句时,语句还没有打印出来系统就暂停了一秒钟,之后行缓冲区刷新,将之前的内容都打印出来, 因此第二个语句才会是先暂停一秒再打印。又因为提示符是跟随光标打印的,所以便跟在语句之后打印。
🥑我们都知道 /n 为换行符,而 /r 又被称为回车,会将光标会到本行的最开始。当我们使用 /n 便会强行刷新行缓冲区,使之前的数据被打印出来。而第三个语句之所以没有打印出语句则是因为 /r 使得光标回到本行开头,提示符从头开始打印将原来要打印出来的语句覆盖掉了,所以没有语句显示出来。
🥑若使用fflush对缓冲区进行刷新的话,便能够看到语句打印出来一秒钟后,就被系统的提示符覆盖打印了。
#include <stdio.h>#include <unistd.h>int main(){ printf("hello world/r"); fflush(stdout); sleep(1); return 0;}
小程序
🥑通过上面第三个语句的启发,我们可以写出一个类似于进度条的小程序,通过每次重复覆盖打印,得到进度条在持续增长的效果。
#include<stdio.h>#include<string.h>#include<unistd.h> #define SIZE 101int main(){ int i = 0; char s[SIZE]; //转换成重复打印一个数组 memset(s,0,SIZE*sizeof(char)); //对数组初始化 const char *lable ="|/-//"; while(i<=100) { if(i!=100) { s[i] = '>'; //i指向最高一位,作为箭头 } printf("[%-100s][%3d%%][%c]/r",s,i,lable[i%4]); //把数组对其打印出来,并附上其他观赏性信息 fflush(stdout); //每次刷新缓冲区 s[i++] = '='; //进度条延长 usleep(100000); } printf("/n"); return 0; }
🥑好了这次调试器 gdb 及 '/r' 的介绍就到这里结束了,关注博主共同进步!!