补一下CSAPP的笔记html
计算机中的整数主要包括两种:有符号数与无符号数。程序员
其中有符号数的表示方法与传统二进制一致。
假设有一个整数数据类型有w位。咱们能够将位向量写成
在这个编码中,每一个xi都取值为0或1。咱们用一个函数来表示B2Uw来表示:
无符号数的编码方式实际上与咱们所知道的二进制编码方式是一致的。惟一要注意的是无符号数的编码具备惟一性,也就是说一个数字只能有一个无符号数编码。这是由于B2Uw是一个双射。函数
有符号数的编码主要有三种方式:原码、补码与反码。我曾经写过一篇博客来进行探究,这里不赘述。
关于补码的由来和做用
须要说明的是:补码也具备惟一性,原码与反码不具有这种性质,由于0在原码与反码中有两种解释。测试
C语言中提供了在不一样数据类型中作强制类型转换的方法,对于无符号整数与有符号整数之间的转换方式,大多数系统上默认的是底层的位不变,由此咱们能推出有符号数与无符号数之间的转换。
关于这些的转换的的过程和原理,在此不赘述。这里直接给出公式:
一个数的编码方式从无符号编码(补码)转换为有符号编码后的数值公式为:编码
若是有符号数的真值小于0那么,把真值加上2w即为其无符号真值,若是真值大于0,那么不变。
咱们用一段C语言代码举例:设计
#include<stdio.h> #include<stdlib.h> #include<limits.h> int main(void) { int i = -1; unsigned int j = (unsigned int)i; printf("%u\n", j); printf("%u\n", UINT_MAX); system("pause"); }
VS2017下的运行结果:
数据类型int的大小为8字节,32位,把-1转换成无符号数须要加上232,结果为232-1,正好为无符号数编码的最大值,因此与UINT_MAX的值一致。3d
直接给出公式:
C语言代码测试实例:code
#include<stdio.h> #include<stdlib.h> #include<limits.h> int main(void) { unsigned int i = UINT_MAX; int j=(int)i; printf("%d", j); system("pause"); }
VS2017下的运行结果:
htm
须要说明的是,在VS2017的环境下,上面两个程序通过测试即便不使用强制类型转换也能够获得正确的结果,其一是C语言中若是发现左右两边数据类型不一致会自动把数据往左边的类型转换,其二是,printf中的格式说明符也会自动执行类型转换,这里使用强制类型转换只是为了让转换看起来更加清晰。blog
因为C语言对同时包含有符号和无符号数表达式的这种处理方式,出现了一些奇特的行为。当执行一个运算时若是它的一个运算数是有符号的而另外一个是无符号的,那么C语言会隐式地把有符号参数强制类型转换为无符号,并假设这两个数都是非负的。
对于 <和> 这样的关系运算符来讲,它会致使非直观的结果。咱们一样用一个C语言程序来做为测试:
#include<stdio.h> #include<stdlib.h> int main(void) { printf("%d", -1 < 0U); printf("%d",(unsigned)-1 > -2); }
VS2017运行结果:
第一个表达式中,因为0是无符号数,因此-1默认变成无符号数,即为232-1,这个数必然比0要大。因此第一个表达式为假。
第二个表达式中,经过把-1强制转换成无符号数,-1变为232-1,-2变为232-2,因此第二个表达式为真。
有时咱们会把一个占用空间较小的数据类型转换为占用空间较大的数据类型(若是把占用空间较大的数据类型转换为占用空间较小的数据类型,可能会丢失数据,咱们通常不推荐这么作)。
定义宽度为w位的位向量:
和宽度为w’的位向量:
其中w'>w。则:
要将一个无符号数转换为一个更大的无符号数数据类型,咱们只要简单的在前面加上足够的0便可,这种运算被称为零扩展。
定义宽度为w位的位向量:
和宽度为w’的位向量:
其中w'>w。则:
要将补码数字转换为一个更大的数据类型,能够执行一个符号扩展(sign-extension),在前面添加最高有效位的值。
具体证实略。
值得注意的点:在C语言中,把类型不一样、大小不一样的两个数据类型相互转换,先改变数据类型的大小,而后在执行类型转换。
好比说:在C语言中,把一个short类型的变量转换为unsigned类型的变量,咱们要先把short类型的变量扩展到8个字节,而后再执行有符号数到无符号数的转换。
一些特殊状况下,尽管这样作会带来风险,但咱们仍然有时候会须要把一个高位的数据类转换为低位的数据类型,这时候咱们就须要截断这个数字。
定义宽度为w位的位向量:
而它截断为k位的结果为:
令x=B2U_w(\vec x),x'=B2U_(\vec x'),则x'=x mod 2^k。
截断为k为实际上就是对原数的真值用2^k取模。具体证实过程略。
要理解有符号数的截断,咱们首先要明白,不管是有符号数仍是无符号数真正区别他们的不是他们的真值,而是他们的编码方式,实际上不管是有符号数,仍是无符号数,在内存中都表示为串二进制数,有了编码对他们真值的解释,他们才能表示不一样的数据。
咱们都知道,截断实际上就是截去前面冗余的位,只留下咱们须要的位,既然无符号数和有符号数在内存中表示的方法实际上都是一串二进制数,咱们为何不能够把一个有符号数的位模式,看作是无符号数的编码,用无符号数的方式将其截断后获得的真值,再用把无符号数转换为有符号数,最终获得将有符号数阶段的真值。
总而言之,有符号数编码的截断结果是:
有符号数到无符号数的隐式的强制类型转换,每每会隐藏许多难以发现的错误,因为这种强制类型转换在代码找那个没有明确指示的状况下发生的,程序员们每每会忽略掉它的影响。 C语言中虽然没有明确规定无符号数的编码方式,但大多数系统和机器都默认使用补码来表示。而且对于无符号数而言,在不少程序设计语言中,并无这个类型,缘由很简单,就是由于有符号数转换为无符号数会带来许多难以察觉的问题。好比JAVA中就不含有无符号类型。