【GDB调试】智慧中控项目的调试
一.在执行的智慧中控项目的时候,喊语音模块唤醒(小欣小欣)的时候遇到了:Segmentation fault
段错误
二.遇到段错误,一般是以下情况:
“Segmentation fault”(段错误)是Linux系统中常见的程序异常终止信号。它通常发生在程序试图访问一个未分配给它的内存区域或尝试以不允许的方式访问内存时。以下是一些可能导致段错误的常见原因:
-
空指针解引用:如果你的程序试图通过一个空指针访问数据,这将导致段错误。
-
数组越界:当你试图访问数组的一个不存在的位置时,例如,数组大小为10,而你试图访问第11个元素。
-
野指针:指向已经释放了的内存空间的指针被称为野指针,使用这样的指针可能会导致段错误。
-
栈溢出:当函数调用层次太深或者局部变量占用空间过大导致栈空间耗尽时,会发生栈溢出。
-
多线程问题:在多线程环境中,如果两个或多个线程同时访问并修改同一块内存而没有适当的同步机制,可能会导致段错误。
-
非法地址操作:如向只读内存写入数据等。
-
内存分配失败后继续使用:当
malloc()
、calloc()
等函数返回NULL值表示内存分配失败时,如果程序没有检查这个返回值而直接使用分配的内存,就会发生段错误。 -
结构体或对象的成员访问:当尝试访问一个未正确初始化的对象的成员时,也可能引发段错误。
解决段错误的方法包括但不限于:
- 使用调试器(如GDB)来定位错误发生的准确位置。
- 检查所有指针是否都被适当地初始化,并且在使用前确认它们不是NULL。
- 检查数组边界,确保不会越界访问。
- 对于动态内存管理,确保释放内存后不再使用该内存。
- 在多线程应用中,确保对共享资源的操作进行了适当的同步处理。
三.我如何解决?这里使用GDB去调试
1. 首先使用GDB去运行项目
用gdb去调试一般就这个步骤:
①在gcc后面加-g编译
②在前面加gdb去执行
③遇到(gdb)的时候,按r运行,再次遇到继续按r
④后面就会看到输出的结果,和程序奔溃的地方(哪个文件,哪行代码)。
2. 回到项目,找到发生错误的地方
①一般段错误就是指针的问题,经常要么是使用了空指针,要么就是指针越界了。
这里发现cur_gdev也是个指针,cur_gdv指针被拿去做了一些操作(被拿去做了if判断)。
并且这里看到cur_gdev一开始赋值是NULL,那这里就应该去看看cur_gdev后续是否有被到赋值。
如果cur_gdev没有被赋到值,又被拿去做了其它其它操作,就会出现段错误,那就是使用一个指向空的指针去跟其它的数据做if判断,就会出错。
所以这里开始怀疑cur_gdev是不是没赋到值?
②所以这里找到cur_gdev被赋值的代码(92行),看看cur_gdev有没有被赋到值。
在cur_gdev被赋值的代码下面加上一条printf语句打印一串数字(93行)。
执行看看会不会输出这行数字。如果输出这行数字,说明程序跑到这个printf语句了,那也就说明cur_gdev被赋值的语句也跑到了,说明cur_gdev被赋到值了。
③用GDB执行结果如下:
没看到输出我们加的那行代码88888888888(代码93行)?
3.继续去调试
①在输出888888的代码,后面加"\n"换行符试试:printf(“88888888888888888888\n”);
②老样子使用gdb编译、执行、按r运行
③老样子,在发生错误的地方,唤醒语音模块(喊小欣小欣)
④结果如下,看到了我们加的那行代码88888888888(代码93行)。说明cur_gdev被赋值的语句(92行),程序是有执行到的
4.继续调试,在有怀疑的地方继续加printf输出语句
①那cur_gdev被赋值的语句确实是有被执行到的,那我们在点进去看看给cur_gdev赋值的函数find_gdevice_by_key()
点进去看看,cur_gdev
有没有可能会被赋值为NULL
。
cur_gdev = find_gdevice_by_key(phead_gdev,recv_msg->buffer[2]);
②看到find_gdevice_by_key()
函数里面,这段代码的意思是:
如果链表头是空就返回空NULL
,
否则就去遍历链表,
遍历完链表都没有找到需要return
的那个p
,我们就返回空NULL
发现确实有两个会返回return
,那cur_gdev
确实是有可能会被赋值为空NULL
的。
所以我们给每个return
前面加个printf
输出语句(记得后面加\n)
然后继续唤醒语音模块(喊小欣小欣),
看看最后会打印出哪个printf
输出语句,那就能侧面知道return
的是什么给cur_gdev
③看到打印的是77777,那说明后面会return
的是NULL
空值给cur_gdev
。证明了我们的猜想。
4.继续调试,在有怀疑的地方继续加printf输出语句
①回到前面,所以刚刚唤醒语音模块(喊小欣小欣)的时候,cur_gdev
确实有可能被return返回一个空值NULL的,那这里却没有对cur_gdev
是NULL的时候做什么处理,导致后面cur_gdev
直接被拿去做if判断了,出现了段错误。
②那这里加个如果cur_gdev
是NULL
的时候的处理看看,继续验证猜想:cur_gdev
是不是真的NULL
。
③结果如下:
看到输出1111111111,再次证明cur_gdev
确实是NULL
。然后后面就被拿去做操作了,所以是空指针操作出现的段错误。
5. 所以按实际需要给cur_gdev做空指针处理,如果cur_gdev为NULL就怎么怎么样。
我这里是给如果cur_gdev是空指针,我就直接退出线程。