Jiang's C++ Space

创作,也是一种学习的过程。

   :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
弄C++很多年了,没想到还居然被这种问题所困,其实不光我了,问了几个同道中人,都未能很好解释为什么,不过我还是记录一下,有知情人士看到的话不妨留言告知。

代码是hello world级别的,很简单:
int _tmain(int argc, _TCHAR* argv[])
{
    
int ni = 50;
    unsigned 
int ui = 100;
    
    printf(
"%d\n", ni-ui);
    printf(
"%d\n", (ni-ui)*2);
    printf(
"%d\n", (ni-ui)/2);
    printf(
"%d\n", (ni-(int)ui)/2);
    
return 0;
}
问题:输出结果是什么?
我预期的输出结果应该是这样:
-50
-100
-25
-25
而事实上是:
-50
-100
2147483623
-25
在VC6,VS2005和VS2008上调试过,结果完全一致,这就表示,(ni-ui)/2的结果被认为是一个无符号整型,为什么会这样呢?我看了一下反汇编……其实,我没看明白。(汇编没学好)

这种小问题可能会引发大问题,我最近在设计一个程序,把图片贴到窗口上,图片的宽和高被我设计为无符号的,因为宽和高最小为0,不可能是负数,而图片的绘制位置则有可能是负数,这跟坐标系有关,这样有符号和无符号之间就有可能出现了上面的那种操作,导致程序出现了一些怪异的行为,通过调试,发现是这个问题。

解决方法很简单,只要加上一个强制转换即可,像代码最后一个printf语句那样。但为什么这样我就不太清楚了,是C++的规范,还是编译器的问题,有其它编译器的朋友可以试试看。
posted on 2010-06-12 10:43 Jiang Guogang 阅读(2012) 评论(7)  编辑 收藏 引用 所属分类: Knowledge

评论

# re: 怪异的有符号/无符号转换问题 2010-06-12 10:46 mr.huang
太诡异。。。麻烦了。。。我们的工程都很少注意这些。  回复  更多评论
  

# re: 怪异的有符号/无符号转换问题 2010-06-12 17:45 OwnWaterloo
printf("%d %u\n", ni-ui, ni-ui);
printf("%d %u\n", (ni-ui)*2, (ni-ui)*2);
printf("%d %u\n", (ni-ui)/2, (ni-ui)/2);
printf("%d %u\n", (ni-(int)ui)/2, (ni-(int)ui)/2);

观察输出结果, 明白了吗?  回复  更多评论
  

# re: 怪异的有符号/无符号转换问题 2010-06-13 10:07 博主
@OwnWaterloo
我的问题如文中所说,“是C++的规范,还是编译器的问题”,(ni-ui)*2被认为是一个有符号的整型,而为什么(ni-ui)/2却被认为是一个无符号整型,是C++的规范,还是编译器的问题?  回复  更多评论
  

# re: 怪异的有符号/无符号转换问题 2010-06-13 10:36 OwnWaterloo
@博主
规范。

二元操作符始终会将操作数转换为同一类型计算。

转换规则很复杂, 但有2点:

1. signed T的rank一定比unsigned T要低
2. int 刚好超过默认参数提升的范围

所以 int op unsigned 一定是都转换为unsignd计算。
  回复  更多评论
  

# re: 怪异的有符号/无符号转换问题 2010-06-20 21:33 gejun
“(ni-ui)*2被认为是一个有符号的整型,而为什么(ni-

ui)/2却被认为是一个无符号整型,是C++的规范,还是编

译器的问题?”

hi,Jiang. 又在默默耕耘啦~~

请问你根据什么知道(ni-ui)*2被认为是一个有符号的整

型而(ni-ui)/2却被认为是一个无符号整型?

如果你是根据printf("%d",XXX)的输出,那就错了。因为

printf("%d",XXX)不管XXX是什么类型都会把XXX当做有符

号数输出的。

我做了个实验证明之前那位网友所说,int op unsigned

一定是都转换为unsignd,是对的。

(写了段代码来证明那位网友的结论:
unsigned int ui = 1;
int ni = 2;

if((ui - ni)<0){
//如果(ui - ni)被转换为有符号数就会进入这

个分支
printf("should not see me.\n\r");
})

回到你说的问题。那输出结果到底是什么?

把你代码中的%d换成%x就真相大白了。

printf("%x\n", ni-ui);
printf("%x\n", (ni-ui)*2);
printf("%x\n", (ni-ui)/2);
printf("%x\n", (ni-(int)ui)/2);

输出结果如下:
ffffffce
ffffff9c
7fffffe7
ffffffe7

那对于你说有问题的那两句话,
(ni-ui)/2输出为7fffffe7
(ni-(int)ui)/2)输出为ffffffe7
为什么会有这个差异?
请看看汇编代码。

在计算(ni-ui)/2时,编译器使用shr来计算结果。shr是

逻辑右移,右移的同时高位填0,所以得到7fffffe7。
在计算(ni-(int)ui)/2时,编译器使用sar来计算结果。

sar是算术右移,右移的同时保留符号位,所以得到

ffffffe7。

那为什么(ni-ui)/2采用shr而(ni-(int)ui)/2采用sar?
我想就像前面那位网友说的,(ni-ui)被转换为无符号数

,所以编译器采用shr忽略符号位;而(ni-(int)ui)被强

制转换为有符号数,所以编译器采用了sar以考虑符号位


  回复  更多评论
  

# re: 怪异的有符号/无符号转换问题 2010-06-21 08:29 pmerofc
(ni-ui)*2被认为是一个有符号的整型,而为什么(ni-ui)/2却被认为是一个无符号整型

(ni-ui)*2 , (ni-ui)/2 都是 unsigned

原因就是OwnWaterloo说的类型转换

至于输出-100,那完全是%d的缘故  回复  更多评论
  

# re: 怪异的有符号/无符号转换问题 2010-06-21 15:58 博主
Thank you all.
我已经了解。总结回来就是:有符号和无符号的运算,必须要小心谨慎一些。  回复  更多评论
  


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理