Chinaunix首页 | 论坛 | 博客
  • 博客访问: 8026570
  • 博文数量: 159
  • 博客积分: 10424
  • 博客等级: 少将
  • 技术积分: 14615
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-14 12:45
个人简介

啦啦啦~~~

文章分类
文章存档

2015年(5)

2014年(1)

2013年(5)

2012年(10)

2011年(116)

2010年(22)

分类: C/C++

2011-10-31 22:41:22

作者:gfree.wind@gmail.com
博客:blog.focus-linux.net       linuxfocus.blog.chinaunix.net

今天再次遇到一个C语言的细节问题,并且发现自己以前的理解不正确,然后总结了一下,写出本文。请看下面的代码:
  1. #include <stdlib.h>
  2. #include <stdio.h>


  3. int main()
  4. {
  5.     unsigned int a = 1;
  6.     unsigned int b = -1;

  7.     printf("a is 0x%X\n", a);
  8.     printf("b is 0x%X\n", b);

  9.     printf("a-b is 0x%X\n", a-b);

  10.     return 0;
  11. }
它的结果为:
  1. a is 0x1
  2. b is 0xFFFFFFFF
  3. a-b is 0x2
其中最后的a-b的结果为0x2,不是我之前期望的。因为a为1,而b为无符号数中的最大数,它的结果怎么会是2呢?

如果有朋友认为结果是2的原因,是因为1-(-1)=1+1=2。那么我只能说恭喜你,虽然你的推导与结果相同,但是想法是错误的。因为这里是无符号数,不是有符号数,所以这里没有-1。

经过和一些朋友的讨论,我又写了一些测试代码:
  1. #include <stdlib.h>
  2. #include <stdio.h>


  3. int main()
  4. {
  5.     unsigned short a = 1;
  6.     unsigned short b = -1;

  7.     printf("a is 0x%X\n", a);
  8.     printf("b is 0x%X\n", b);

  9.     printf("a-b is 0x%X\n", a-b);

  10.     return 0;
  11. }
这里只是将int改为short型,结果则是:
  1. a is 0x1
  2. b is 0xFFFF
  3. a-b is 0xFFFF0002
先分析一下这个结果,看看是否对于之前的问题有所帮助。a-b的时候,按照c标准会将其变为int型,即(int)a-(int)b=1-65535=-65534=0xFFFF0002。而%x打印的是无符号整数,结果自然是0xFFFF0002。

第二份代码与第一份代码,不同之处,只是a和b的类型不同。在第一份代码中,a-b的时候,因为其类型为无符号整数,所以这里不会发生整数提升,仍然是无符号数的减法。而1-0xFFFFFFFF应该等于-0xFFFFFFFE。而这个数值实际上可以理解为超出了无符号数unsigned int的表示范围——注意,这里表达的并不准确,因为unsigned int的范围是0~0xFFFFFFFF。也就是说unsigned int是无法表示-0xFFFFFFFE的数值的。

为了证明这一点,我们可以扩大一下a和b的类型:
  1. #include <stdlib.h>
  2. #include <stdio.h>


  3. int main()
  4. {
  5.     unsigned int a = 1;
  6.     unsigned int b = -1;

  7.     printf("a is 0x%X\n", a);
  8.     printf("b is 0x%X\n", b);

  9.     printf("a-b is 0x%llX\n", (unsigned long long)a-(unsigned long long)b);

  10.     return 0;
  11. }
其结果为:
  1. a is 0x1
  2. b is 0xFFFFFFFF
  3. a-b is 0xFFFFFFFF00000002
这与我们前面的分析吻合。

从上面的分析看出,这个简单的无符号整数的减法操作,引出了这么多的东西。如果说前面的分析或者推导有些难以理解的话,还有一种理解方式,但是我不知道正确与否,合适与否。这个a-b的值一定要满足a-(a-b)=b。当a=1,而b等于0xFFFFFFFF时,所以这个(a-b)一定为2。——你不要觉得这样的等式一定成立,在计算机的编程中,这种算术运算的反运算,是有可能不成立的。

今天有看了看二进制补码以及减法的实现——依然是通过加法实现,感觉对这个问题似乎可以理解的更深。但是目前还是有一层窗户纸,没有捅破,就差一点了。


阅读(35829) | 评论(9) | 转发(5) |
给主人留下些什么吧!~~

GFree_Wind2012-09-21 13:57:29

zhangyd6: 在对于tcp squence number的处理上,内核有如下的函数,使用的很巧妙,两个unsigned int 相减,sub 命令如何对于unsinged 处理,兄台如果有新的想法,希望请教一.....
这个其实很简单。——我个人觉得不算巧妙。

你要明白其实加减运算对于CPU来说,都是一样的,其实都是加法运算。
这也就是为啥,负数要用二进制补码的形式。

那么对于CPU的运算单元来说,其实没有什么有符号无符号之类的。对于它来讲,它看到的就是普通的二进制数。
至于这个二进制如何解释,是上层的事情。

zhangyd62012-09-21 11:58:14

在对于tcp squence number的处理上,内核有如下的函数,使用的很巧妙,两个unsigned int 相减,sub 命令如何对于unsinged 处理,兄台如果有新的想法,希望请教一下。

static inline int before(__u32 seq1, __u32 seq2)
{
        return (__s32)(seq1-seq2) < 0;
}
#define after(seq2, seq1)         before(seq1, seq2)

GFree_Wind2012-03-21 16:41:00

TestForCU: 呵呵,你用的编译器是什么东东。。
编译器这都不检查?unsigned int b = -1;
这个就一补码的理解,计算机中就一全加器。。补码的引入基本上操作都是由全加器完成.....
GCC编译器。
这里我用-1,只是有点犯懒,目的是为了将b赋值为0xFFFFFFFF。

这个就一补码的理解,计算机中就一全加器。。补码的引入基本上操作都是由全加器完成的。。当然现在都有什么硬的乘法除法器之类的——恩,说得对。实际上根本没有减法器。所有的减法都是通过加法来实现的。

TestForCU2012-03-21 14:29:03

呵呵,你用的编译器是什么东东。。
编译器这都不检查?unsigned int b = -1;
这个就一补码的理解,计算机中就一全加器。。补码的引入基本上操作都是由全加器完成的。。当然现在都有什么硬的乘法除法器之类的