六脉shen剑

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

今天遇到一个问题,系统有时执行正确,有时执行错误。关键代码如下

TYPE *res = NULL;

TYPE mydata;

res = search(arg1, arg2,  &mydata);

if(res <= 0)

{

  printf("search return value is not valid!!!!!\n");

  return -1;

}

其中,search函数的大致过程如下:

TYPE *search(int arg1, int arg2, TYPE *data_ptr)

{

  if(...)

  {

    if(...) return 0;  

    else if(...) return -1;

  }

  else

  {  

    data_ptr->.. =..;

    return data_ptr;

  }

}

按照楼主的想法,search执行出错时,返回值为非正值。执行成功,返回正值,因为地址一定非负。

但是程序运行过程中对于同样的参数输入,search有时返回值为非正值,有时为正值,楼主自然以为search函数的返回值很可能被破坏掉了,导致时好时坏。

折腾了一天,又是添加printf语句又是对指针强制类型转换的,最后都没有解决问题。

后来,一个同事过来看了看,让我改变判断条件。将对返回值的判断改为下列语句:

if(-1 == res || 0 == res)

{

  printf();

  return -1;

}

做了这样的修改后,程序奇迹般的不再出现时好时坏的情况了。此时楼主恍然大悟,即使search执行正确,返回值也有可能是非正值。 

因为,返回值是个变量地址。 

学过操作系统的人都知道,程序执行过程中用到的地址都是虚拟地址,是由操作系统经内存映射生成的。 理论上讲,这些地址可以是指针类型大小所能表示的任意值。对于32位机器而言,这个值的变化范围为0~0xFFFFFFFF;对于64位机器而言,这个值变化范围为0~0xFFFFFFFFFFFFFFFF。 所以,变量的地址可以是程序所在平台的任意值。

楼主的机器是64位机,指针类型变量大小也为64位,int型变量大小为32位。

考虑以下情况,

(1)mydata变量的地址为0x0000FFFF89765431, 当与0相比较时,比较运算符会自动识别变量类型,并会对变量两边类型自动进行转换,如类型提升。 此时,res的值确实大于0,但如果将改值强制转换为int型后再与0相比较,就会得出小于0的结果。

原因在于,64位机器上指针类型大小为64位,int型大小为32位,这种强制类型转换会导致高32位丢失,得到的值实际上为0x89765431。 最高位是1,且为int型,所以小于0。

(2)mydata变量的地址为0xF0000720005674A6, 当与0相比较时,比较运算符为64位数之间的“无符号比较”,地址也为正值。由此,当mydata为-1时,此时它的十六进制表示为0xFFFFFFFFFFFFFFFF, 由于指针和整数直接比较时指针被视为无符号整数,故此时mydata实际为最大整型值,if判断不成立。

 

通过以上分析,采用对search不同情况下的返回值进行判断,而不是将非正值视为search执行失败的情况,只要变量地址不为NULL且为0xFFFFFFFFFFFFFF,则一定可以保证if判断语句的正确性。

总结原来程序失败原因,有下:

(1)对指针值与整型比较时,指针值的类型不清楚,经验证,楼主的64位机将指针及与指针相比较的数视为无符号长整型。

(2)相当然的认为指针值不会超过32位。64位机,指针可以为0~0xFFFFFFFFFFFFFFFF间的任意值。用int型强制转换会引起精度丢失。

(3)没有对search的返回值进行详细利用,无意间扩大了返回值的代表的信息。 原本search对返回0和-1的情况认为是失败情况,而楼主将其扩大为所有非正值都是失败情况,这是引入错误的根源。

 

经过这番过程,楼主深深的感到,学问还需仔细推敲,不是相当然就可以的。 谨以此记告诫自己和如楼主一样粗心大意的IT工作者们,编程还是要有扎实的基本功和良好的推敲习惯,正所谓 纸上得来终觉浅,绝知此事要躬行!

posted on 2014-03-19 21:43  六脉shen剑  阅读(1894)  评论(0编辑  收藏  举报