简单的代码,复杂的问题。
环境:VC6SP5,Win2kPro
int b = 10; //已经赋值为有效数据
const char * str = "1234567890ABCDEF"; //已经赋值为有效的字符串。
int a = (b - strlen(str) ) / 2;
经过了这样的计算,a的值究竟是多少?
结果当然不是 (10-16)/2 的 -3,而是一个天文数字。。。。
a = 2147483645 或者,16进制的 0x7ffffffd
真正的问题代码只有一行,但是结果却令人不快。
这里分析一下产生这个问题的原因:
一个隐含的问题是,strlen的返回值,类型是size_t,也就是unsigned int。这是个无符号类型。
当unsigned int 和 int混合运算的时候,编译器会认为,unsigned int的表达长度比较长,因而结果的类型是unsigned int。
所以 b - strlen(str) 的结果不是 -6,而是和-6具有相同内存布局的unsigned int 值:0xfffffffa,也就是正的4294967290
如果这个数值直接赋值给int,会因为内存布局相同,而避免问题的产生--问题存在,但是不产生不良效果。但是随后的/2操作把问题彻底暴露出来了。。
/2操作可以认为是一次移位,对于无符号类型,移位后高位补0,于是把原本有符号类型赖以利用的符号位清零了。。最终这个数值赋值给int类型的时候,结果就变成了前面提到的那个可怕的数值。。
有符号数字和无符号数字混合运算的时候,存在一些默认的转换规则,符合这些规则的转换,都不会引发编译器警告或者错误,因此这类问题是难以觉察的。但是,我们需要经常使用的库函数太多了,当参与运算的时候,为每个函数调用都指定一个固定类型的返回值,似乎是不可能的任务。因此如果你的程序中充满各种不同类型的整型运算,那么还是尽量小心一些吧。。。