数据断点调试

全局变量的使用与数据断点调试 (2007-05-19 15:42:03)
    先前参与的项目,虽然使用的是面向过程的C语言,但软件框架设计,模块的封装,数据处理独立于菜单实现等等,使得软件具有面向对象的优点。面向对象可以使我们尽量的避免使用全局变量。因为学校的老师,公司的前辈都在强调全局变量在面向过程中的隐患。而如今我接手的项目,里面却使用了很多全局变量,软件完全是面向过程的设计方法。突如其来的这么多全局变量,让人感觉不适应,而恰恰让我遇上了一次全局变量带来的问题。记录和总结一下问题的解决过程。
       在处理一个问题的时候,发现一个全局变量(char TempStatus;)的值偶尔出现不是我所期望的,于是向前追溯,找到最后一个赋值语句 TempStatus = 1;在这一过程中没有再修改过,在赋值后加打印,竟然出现前后连续两个printf输出的该变量的值不一样的情况,更别说后面再用了。出现这样的情况,有两种可能,一是该全局变量在别的进程中被修改了;二是与该全局变量地址接近的某个全局变量出现越界了。要是第一种情况还比较好处理,如果是第二中情况,那就非常棘手了。
      先排除第一种情况,要确认是否是在别的进程被修改了,就需要确认在什么地方修改的。搜索该全局变量,好家伙,出来一大屏幕,汗!!看一边都得半天,要在每个地方加一个printf逐一排除,那得多长时间啊,郁闷!思路受阻。此时不禁怀恋set与get的好,你说要是写上以下这样的两个函数给别的地方调用该多好啊!
void SetTempStatus(char val)

      TempStatus = val;

 
void GetTempStatus(voild)

     return TempStatus;

同时将TempStatus 定义为static char TempStatus;在要用的时候调用get函数,在要赋值的地方调用set函数。这样一来,我要确定是否有别的地方修改就只要在set函数里面加个printf就可以了,确定是被别的地方修改后只要在set函数里面加个断点,一单步就立刻定位修改的地方了。多简单!可实际项目中并没有这样设计,没办法了。
         那就排除溢出情况吧。一般溢出都是该全局变量前的变量越界操作引起。于是找到该变量定义的地方:
int iDataSize;
char TempStatus;
unsigned int DataLen;
struct_table   DataTable[40]={0};
stuct_table1  DataTable1[20];
前面是一个整形数据,出现越界操作的不太可能,莫非是后面的数组?一搜索,又是一大屏,而且难度更大,你不知道它什么时候在什么地方出现越界溢出,汗,汗!!!
       就这样整整郁闷了大半天,没法确定问题的根源,也就没法彻底解决问题,毫无进展。脑门子冒汗了,休息休息再说,还别说有的时候无路可走了,放松一下回来真有豁然开朗的感觉。全局变量的值不是我期望的,而我自己没有做赋值,那肯定就是被修改了,不管是什么原因,最终就是要确定它在什么地方被修改的。于是想到了,项目方案的一个可视化工具里面有一个数据断点(DataBreakpoint)的功能,也许使用这一功能也许就能找到意外修改的地方。
      可视化工具在工作中很少有同事用,数据断点就更少有人用。这也是我第一次用,凭借一点理解试一下。理解中数据断点,应该是对某一变量的地址所对应的空间进行监控,一旦该地址对应的空间被修改,就会立即产生中断,并将程序的运行指针指向修改的语句。由此肯定能确定问题所在。
      因为全局变量在很多地方都有赋值,如果一开始就给该变量设数据断点(DataBreakpoint),肯定会产生很多中断。而我只需要关注该变量在我赋值以后的变化情况,由此在我的赋值语句TempStatus = 1;处先设一个代码断点(Code Breakpoint)让程序先运行到这里,然后设置该变量的数据断点,让程序继续运行,这个时候一个很奇怪的中断出现了,程序的运行指针指在语句 DataTable1[iIndex].value = 0;程序在一个毫不相干的赋值语句中断了,那肯定是数组DataTable1越界修改了TempData对应的空间,喜!再看该数组的下标,竟然是-1。问题找到了,再看看该语句所在的函数:
int GetDataIndex(char data)
{
    .....
    for(i=0;i<DataLen;i++)
     {
        if(DataTable[i].value == data)
           return i/2;
     }
   return -1;
}
void  CreateTable(void)

     int iIndex;
       ........
     iIndex = GetDataIndex(data);
      .........
     DataTable1[iIndex].value = 0;

一看问题出来了,函数CreateTable()没有对变量iIndex作出错处理,在iIndex出错等于-1时导致后续的数据越界操作。向前越界结构体stuct_table1的大小,刚好使得value指向了TempStatus。问题解决就很容易了,只要将函数改成:
void  CreateTable(char data)

     int iIndex;
       ........
     iIndex = GetDataIndex(data);
     if(-1 == iIndex)
         return;
      .........
     DataTable1[iIndex].value = 0;

往往是这样,找问题的根源要用九牛二虎之力,而解决问题只需吹灰之力;
      解决了问题,回过头来,看看变量的定义。TempStatus 和DataTable1之间还隔了一个很大的数组,他两怎么会“勾搭”上的?做个实验,打印这些变量的地址,发现数组DataTable的地址与其他几个有很大区别,有同事说,是因为它被初始化赋初值了,跟其他几个全局变量不在一个区域。这样也就好理解为什么DataTable1能够跨过一个大数组跟TempData扯上关系。
      问题解决了,总结一下。一:还是要强调尽量避免使用全局变量,如果非用不可,毕竟还是面向过程设计,应该想到使用set和get,并用static定义加以限制。这样可以减少不少的麻烦,也便于以后查找问题;二,就是要确保函数的健壮性,要有意识的去考虑出错的情况,并做出正确的处理。这样的处理是非常有必要的,往往一些隐患就在于没有对出错情况做出正确的处理;三,就是数据断点的使用,这是个好东西,第一次使用就深深体会了它的好。
(注:这里用到的代码非实际代码,只是为说明问题,出之随意)
 
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值