1 信息存储
计算机字长,指明指针数据的标称大小。32位机器虚拟地址空间为4GB。64位为16EB。64位机器可以运行32位机器编译的程序,反过来不行。
C语言各种数据类型字节长度如下:
寻址和字节顺序:多字节对象在内存中的存放方式分为小端法和大端法。例如一个int有4个字节[x4,x3,x2,x1],x4位最高位,若x4在最前面(低地址)则为大端法,若x1在最前面则为小端法。大多数Intel兼容机都只用小端模式(也就是低地址放低位,高地址放高位)。大多数IBM的使用大端。
大小端产生的问题:1)在不同类型的机器之间通过网络传输二进制数据(用协议解决)2)在阅读表示整数数据的字节序列时。3)编写规避正常的类型系统的程序时。
字符串表示:字符串被编码为一个以null(‘\0’)字符结尾的字符数组。每个字符用某个标准编码来表示,常见的为ASCII字符码。扩展有Unicode,UTF-8,其中UTF-8兼容了ASCII码。
代码表示:不同机器的指令编码是不同的,一般二进制代码是不兼容的。
布尔运算:~非,&与,|或,^异或,位运算。
逻辑运算:逻辑运算认为所有非0参数表示TRUE,0表示FALSE。对应OR(||)、AND(&&)和NOT(!)。如果第一个参数求值就能确定表达式的结果,那么逻辑运算符就不会对第二个参数求值。
移位运算:左移,x<<k,丢弃最高的k位,右端补k个零。右移分为逻辑右移和算术右移。逻辑右移在左端补0,算术右移在左端补最高位,在有符号的整数(最高位为1)移位时有区别。编译器默认为算术右移。当一次移位的k大于数的位数w时,C语言标准是移动k%w。
2 整数表示
无符号整数直接用其二进制数值表示。
有符号整数用补码编码,最高位为符号位,1表示负数,0为非负。负数的补码为原码的反码加1,正数的补码为本身。补码表示的范围,以4位为例,1000表示最小(-8),最大为0111(7),1111表示-1,0000表示0。
可以看出补码的范围是不对称的,|Tmin|=|Tmax|+1,C语言中文件<limits.h>定义了一组常量,来限定编译器运行时整数的范围,有INT_MAX,INT_MIN,UINT_MAX等。
为了保证程序的兼容性,ISO C99标准在文件stdint.h中引入了整数类型类,定义了如int32_t和int64_t这样恒定长度的整数类型。
在C语言中,允许强制类型转换,转换规则是数值可能会变化,位模型不变。例如下面代码:
int i = -1;
unsigned int v = (unsigned int)i;
cout<<v;
输出是4294967295(unsigned int的最大值)。因为-1的补码表示为0xFFFFFFFF,看做无符号数则为4294967295。在执行一个运算时,如果一个运算数为有符号的而另一个为无符号的,C语言会隐式的将有符号的转为无符号的,并假设这两个数非负。这在比较大小时会产生问题。
在-1<0U中,-1转为无符号数为4294967295,其大于0。
将一个无符号数转换为一个更大的数据类型,直接补零就可以,这叫零扩展。将一个有符号数转换为一个更大的数据类型,补符号位,叫符号扩展。
3 整数运算
无符号加法:溢出直接去除高位。
有符号加法:当做无符号数进行加法,溢出直接截断。
有符号的非:-x=~x+1,即等于按位取反再加1.
有符号数的乘法:x*y=(x的补码*y的补码)的补码
整数乘以常数:用移位和加法以及减小来代替乘法,无论x是无符号的还是有符号的,其计算结果一致。
整数除以常数:思路与乘法一致。有符号整数移位在小于0时会出现问题,当x<0时,x>>k的结果是x/(2^k)的向下取整,例如-12340>>4结果为-772,然而-12340/16应该是-771,这时C语言对其进行修改:
(x<0?x+(1<<k)-1:x)>>k
在x<0的情况下,加上(1<<k)-1的偏置量。
4 浮点数表示(IEEE标准)
符号位s:复数s=1,正数s=0
尾数M:是一个二进制小数
阶码E:对浮点数加权,权重为2,可以为负数。
将这三个数划分为三个字段:以float(32位)为例,s为最高位,30-23(8位)表示阶码E,剩下的22-0表示尾数M。double的E为11位,M为52位。
规格化值:在E不全为0,也不全为1。阶码字段被解释为以偏置形式表示的有符号数,表示的值为E=e-(2^(k-1)-1),e为阶码表示的无符号数值,k为阶码E的位数,float为8。小数字段被解释为小数值f,尾数定义为M=1+f,例如001表示二进制数0.001,十进制为1/8,则M为9/8。
非规格化值:阶码全为0,用来表示0和比较小的数。阶码值E=1-(2^(k-1)-1),阶码值的规则和规格化数不同是因为要使得非规格化数过渡到规格化数时平滑,尾数为M=f。
特殊值:阶码全为1,尾数为0时表示无穷大,其他表示NaN(不是一个数)。
8位浮点格式例子如下,k=4,n=3.
浮点数的舍入:从上述浮点数的表示可以发现只能近似地表示实数运算,因此需要舍入。默认为向偶数舍入法。
在C语言中,有两种浮点数float和double,舍入方法为向偶数舍入,不能修改舍入方法,在标准方法中不能得到诸如-0,无穷或者NaN的特殊值,在math.h中有这些值。
整数和浮点数之间转换规则:
int转float不会溢出,可能被舍入。
double转float有可能溢出,可能被舍入。
float或double转int会向0舍入,例如1.9转为1,-1.9转为-1.可能溢出。