技术头条 - 一个快速在微博传播文章的方式     搜索本站
您现在的位置首页 --> 算法 --> 一起空指针引发的程序问题的排查过程

一起空指针引发的程序问题的排查过程

浏览:1890次  出处信息

   【文章摘要

   在C程序中,指针操作是难点和精华所在。指针一旦使用不当,极有可能造成程序的崩溃。

   本文对一空指针引发的程序问题的排查过程进行了详细的介绍,为相关软件问题的分析及解决提供了有益的参考。

   关键词

   日志 指针 C语言 排查

   一、问题描述

   最近,某程序在测试过程中突然崩溃。日志中出现如下内容:

   #0 0xf64f2b3a in FunctionA(event=666, dlgindex=0, ucErrNo=1 '\001') at src/A.c:6838

   #1 0xf64e3a4f in FunctionB(in=0x80efd4d "") at src/A.c:2781

   #2 0xf64dba3b in FunctionC(in=0x80efd4d "", out=0x0, dataPtr=0x0) at src/A.c:303

   二、日志内容分析

   通过以上日志文件内容,我们可以看出如下信息:

   第一,程序最终是在FunctionA函数中崩溃的,该函数有三个入参:event、dlgindex和ucErrNo。其中,出问题时的event为666,dlgindex为0,ucErrNo为1。

   第二,调用FunctionA的是FunctionB函数,该函数只有一个入参:in。

   三、问题的排查过程

   通过对日志内容的分析,我们查看了event为666的程序代码,主要用于从数据库中获取某几个系统参数的值。其整个执行流程如图1所示。

   1

图1 程序执行流程

   通过图1,我们可以看到,程序问题出现在了数据库在设定的时间之内没有应答而进入超时处理的流程中。

   我们又仔细查看了日志信息,发现出问题的地方在“A.c”文件的第6838行。该行及前一行代码如下:

   pReq = (T_RetInfoFromReqMsg *)DlgBuf[dlgindex].para; (第6837行代码)

   flowid = pReq->flowid;                                                        (第6838行代码)

   第6837行代码对pReq指针赋值,第6838行代码将pReq对应的结构体中的flowid变量赋给整型变量flowid。

   看到这里,我们就怀疑“DlgBuf[dlgindex].para”指针的值有问题。我们又仔细查看了程序代码,发现该指针是在向数据库发消息的函数中被赋值的。其代码如下:

   UINT32 SendToDB(…, UINT8 *para, UINT32 paraLen)

   {

      ……

      if ((para != NULL) && (paraLen > 0))

      {

         memcpy(DlgBuf[iDlgIndex].para, para, (int)paraLen);

      }

      ……

   }

   整个DlgBuf是在程序刚启动的时候被初始化的。我们又看了调用SendToDB函数的代码行,如下:

   SendToDB(…, NULL, 0);

   可以看到,传入的para指针的值为空(NULL),paraLen的值为0。根据SendToDB函数的代码流程,DlgBuf[iDlgIndex].para不会被赋值,那么它就会为空(NULL)。

   这就是程序问题的原因所在。向数据库发消息的时候,DlgBuf[iDlgIndex].para指针为空(NULL),那么在处理数据库超时的流程中,因为序列号是同一个,则DlgBuf[dlgindex].para指针也为空(NULL),这也导致pReq指针为空。后面要用到该指针对应的结构体中的值,就会找不到,因此程序便崩溃了。

   为了验证我们的想法是否正确,我们修改了调用SendToDB函数的代码,不传空指针进去,如下:

   T_RetInfoFromReqMsg tRetInfoFromReqMsg = {0};

   ……

   SendToDB(…, &tRetInfoFromReqMsg, sizeof(T_RetInfoFromReqMsg));

   对修改后的程序进行测试,就不会出现问题了。

   四、总结

   通过本次问题排查,我们总结出的经验有以下几个:

   (1) 对于指针的使用,一定要小心。对于重要的指针,一定要先检查其是否有准确的值之后再使用,避免使用空指针。

   (2) 不管是在哪个函数中,在使用指针之前,一定要先检查该指针是否为空(NULL)。这也算是对程序进行异常保护。

   程序问题在所难免,但解决问题的方法也是千变万化。确实,只要我们善于总结,善于分析,那么任何程序问题都是可以解决的。

建议继续学习:

  1. Linus:利用二级指针删除单向链表    (阅读:11132)
  2. C语言结构体里的成员数组和指针    (阅读:4579)
  3. 通过引用计数解决野指针的问题(C&C++)    (阅读:3354)
  4. C 语言中统一的函数指针    (阅读:2962)
  5. cpp智能指针的简单实现    (阅读:2919)
  6. 重构发现:指针操作问题    (阅读:2271)
  7. 空指针的解引用    (阅读:2152)
  8. 关于类成员函数指针的正确写法    (阅读:1918)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
© 2009 - 2024 by blogread.cn 微博:@IT技术博客大学习

京ICP备15002552号-1