Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1826423
  • 博文数量: 159
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 5649
  • 用 户 组: 普通用户
  • 注册时间: 2013-01-23 18:56
个人简介

将晦涩难懂的技术讲的通俗易懂

文章分类

全部博文(159)

文章存档

2019年(13)

2018年(19)

2017年(9)

2016年(26)

2015年(18)

2014年(54)

2013年(20)

分类: C/C++

2014-04-22 00:05:19

写在前面:本来因为一个朋友问我为什么可以给unsigned int赋值负数,我打算写一篇关于解释unsigned的文章。但是写的过程中发现有很多地方需要涉及其他的知识点,特别是关于整型提升和算数转换。所以就翻了一下之前看过的书,做了一个总结,感觉自己又学到了不少。由于我不是写教科书,有些用语和描述难免不够准确。另外,由于本人能力有限,有错之处在所难免,希望各位看到此文的朋友如果发现有什么错误请留言指正,或发邮件交流。

(电子邮箱:lvyilong316@163.com  )                  ——转载请注明出处,lvyilong316

注意:doublefloat都不可用unsigned,short修饰,另外float还不可用long修饰

3.1 unsigned 到底有什么用

首先我们先看一段程序:

int _tmain(int argc, _TCHAR* argv[])

{

    int a=2,b=-5,r;

unsigned int ua=2,ub=-5,ur;

bool bo;

printf("a: %d,ua: %d\n",a,ua);

printf("a: %u,ua: %u\n",a,ua);

printf("b: %d,ub: %d\n",b,ub);

printf("b: %u,ub: %u\n",b,ub);

bo=(b==ub);

    printf("bo: %d\n",bo);//1

bo=(ub>a);

printf("bo: %d\n",bo);//1

r=a+ub;

printf("a+ub: %d,a+ub: %u\n",r,r);

ur=a+ub;

printf("a+ub: %d,a+ub: %u\n",ur,ur);

return 0;

};

首先,查看数据的内存表示(十六进制),发现相同的赋值intunsigned int的内存表示

是一样的。但是十进制不同,也就是说程序对相同内存数据的解释不同。

           

十六进制                                       十进制

    那么给unsigned 赋于负值是什么情况?从上面也能看到对于内存来说其实和signed是一样的。我们知道计算机是以补码表示数字,所以-5无论负值给谁,他在内存中的表示都是固定的,signedunsigned仅仅表名对与其所修饰的变量,这段内存是如何解释,即把最高位当做符号位还是最高数值位。

从程序输出的前四行可以看到结果也完全相同。我们知道:signedunsigned的作用相同,%d,%u的不同就在于对内存的解释不同,前者将内存中的数据看成有符号的,后者看成是无符号的。(将signed int使用%u输出,实质就是相当于对这块内存的重新解释)。


结论:相同赋值的intusigned int在内存中的表示是一样的,signedunsigned只是告诉程序对内存的不同解释方式,前者按照有符号数解释(考虑符号位),后者按无符号数(不考虑符号位)解释,这一点和%d,%u的作用是一样的。

    接下来看输出的后面四行,b==ub:好理解,两个数都属于intsigned不同算数转换为unsigned,内存相同,解释相同(同为unsigned),所以相等。我们看下面一个。ub>a(-5>2):这是什么情况?同样还是算数转换,都按unsigned解释,当然-5对应的数大了

继续往下看,r=a+ub;这个又做了什么?记住计算机所有加减运算都是按照补码进行,也就是我们平时说的有符号数。所以无论是signed int之间运算,还是unsigned int之间运算,还是signedunsigned之间运算,都是对用的有符号数运算,再根据结果是赋值给signed还是unsigned做相应解释,但无论赋值给signed类型还是unsigned类型,运算结果的内存表示是一样的。

3.2 signedunsigned的另一区别

接下来看下面一段程序:

int _tmain(int argc, _TCHAR* argv[])

{

   char a;

   unsigned char ua;

   int i,j;

   unsigned int ui,uj;

   a=200;

   ua=a;

   printf("a: %d,   %u,   %x\n",a,a,a);

   printf("ua: %d,   %u,   %x\n",ua,ua,ua);

   i=a; j=ua;

   ui=a;uj=ua;

};

我们知道char的表示范围在-128-127unsigned char0-256;所以我们看看给char赋值一个超出其表示范围的数(200)会是什么效果。

首先,看下aua的内存表示,如下图:


这和预想一样,相同赋值的aua的内存表示也相同。我们再看下输出:


首先,第一列(按照十进制输出),对于achar型),200超过了其表示范围,因为200对应的二进制表示为1100 1000,已经占用到了最高位(符号位)。按十进制输出时把最高位符号位(1)解释为负数。后面100 1000正好是56注意:变量的输出与变量是unsigned还是unsigned无关,而取决于%du%等对内存的再解释。

第二列是什么情况?没错,类型提升a按照有符号数扩展,高位补符号位,ua按照无符号扩招,高位补0?我们看下面几句的赋值情况:

  i=a; j=ua;

  ui=a;uj=ua;


对比iui,可以得出结论:有符号类型(char)无论向有符号类型(int)还是无符号类型(unsigned int)扩展,都会按照有符号数的扩展规则(高位补符号位)。

对比juj,可以得到结论:无符号类型(unsigned char)无论向有符号类型(int)还是无符号类型(unsigned int)扩展,都会按照无符号数的扩展规则(高位补0)。

这就得到有符号数和无符号数的有一个不同点——扩展规则不同。

练习:

     unsigned char a = 0xA5;//a=1010 0101

     unsigned char  b = ~a>>4;//b=1111 0101,注意a要先做整型提升,~的优先级大于>>

     unsigned char  c = a>>4;//c=0000 1010

     c = ~c;//c=1111 0101

     unsigned char  d =~a;//d=0101 1010

     d = d>>4;//d=0000 0101 也要整形提升扩展

 unsigned char e = ( (~a)>>4);//e=1111 0101

3.3 慎用unsigned

看下面例子:

int d = -1; 
if (d <= sizeof(arr)/sizeof(arr[0])) 
... 
这样的比较语句有问题,sizeof运算符返回无符号数。 
if语句在signed intunsigned int之间测试相等性, 
按照前两节讨论的,这里会发生算数转换,也就是有符号的d会被解释成unsigned这样,-1就变成一个非常巨大的正整数,导致比较结果与预期的不符。 解决的方法是使用强制转换,(int)(sizeof(arr)/sizeof(arr[0]))。 

     所以,不要因为无符号数不存在负值而用它表示数量(如年龄、国债等), 尽量使用int之类的有符号数,这样在混合运算中, 这样就不必担心边界情况(如-1被翻译为非常大的正数)。 只有在使用位段和二进制掩码时,才使用无符号数。 应该在表达式中使用强制类型转换,使所有的操作数均为有符号数或无符号数, 这样就不必由编译器来选择结果的类型。 


阅读(2187) | 评论(0) | 转发(2) |
1

上一篇:“C”中的算数转换

下一篇:Epoll模型详解

给主人留下些什么吧!~~
评论热议
请登录后评论。

登录 注册