信息存储

字数据大小

为了避免由于依赖“典型”大小和不同编译器设置带来的奇怪行为,IOSC99引入了一类数据类型,其数据大小在不同机器上是固定的,不随编译器与机器设置而改变。
int32_t, int64_t, uint32_t, uint64_t等.

寻址和字节顺序

假设一个程序中的对象由w位的数字来表示,且w是8的倍数(一个Byte是8个bit),那么就把它从最高位到最低位每8个划成一个字节。
在存储时,有的机器采用从最低有效位到最高有效位来存储(小端法),有的则相反(大端法),这两种方法没有性能上的差别,只是方式不同。
Linux 与 Windows都使用小端法来储存数据。

二进制代码迁移性

将一段程序经过编译后可以发现,在不同的机器上会得到不同的二进制代码,因为不同的操作系统对指令的编码规则是不一样的,因此二进制编码很难在不同的操作系统上兼容。

位级运算

与、或、非、异或被成为位(bit)级运算.
确定一个位级运算的结果的最好方法就是将其转化为二进制数,进行计算后再转化回来。
一个利用异或特性来实现inplace_swap的算法如下:

1
2
3
4
5
void inplace_swap(int *x, int *y){
*y = *x ^ *y;
*x = *x ^ *y; //*x = *x ^ *x ^ *y = *y
*y = *x ^ *y; //*y = *y ^ *x ^ *y = *x
}

不过这种方式并没有性能上的优势。

逻辑运算

在逻辑运算中,所有非0的参数表示True, 0表示False.
并且,如果对一个参数求值,如
if(A || B), 如果只运算A就可以得到确定的结果,那么B就不会被运算。
因此我认为在写程序时要注意,最好不要将对程序结果有影响的式子放在逻辑运算中。

移位运算

左移:在右端补0
逻辑右移:在左端补0
算数右移:在左端补最高有效位的值。对有符号整数的运算很实用。
对绝大多数编译器,对有符号数进行算数右移,对无符号数进行逻辑右移。

整数表示

无符号数编码

可以用向量的思想来理解,每一位都是一个长度为2^i次方的向量,则无符号数字的值即为每一位向量的和。

有符号数的补码编码

对于一个w位的向量 x = [Xw-1, Xw-2, Xw-3 ... X0], 第一位为符号位,权重为(-2^w-1),而这个值足够大到加上之后的所有正数还是负数。因此符号位为1时,值为负,为0时,值为正。

则最小数为:-2^w-1, 即为只有第一位为1的,其他都为0的情况。
最大数为:[01111…]情况,此时为2^w-2 + 2^w-3 + ... + 2^0, 即为2^w-1-1
因此Min = -(Max+1), 且UnsignedMax = 2*MaX + 1

有符号数和无符号数的之间的转换

对大多数C语言编译器,无符号数强制类型转换为有符号数在 底层的位 上不会有改变,只是对每一位解释方式做了改变。

扩展一个数字的位表示

一个较大的数据类型是不可以转换为一个较小的数据类型,但是一个较小的数据类型是可以转换为一个较大的数据类型的,对于有符号数跟无符号数有不同的策略。

对于无符号数的扩展,直接在前面补零即可。
x = [uw-1, uw-2, ……, u0] ===0扩展===> [0, 0, 0, …, uw-1, uw-2, uw-3 …… u0]

对于有符号数(补码),则在前面补最前那一位数字。
x = [w-1, w-2, ……, w0] ===补码扩展===> [w-1, w-1, w-1, w-1, …… w-1, w-2, w-3, ……, w0]

有关有符号数和无符号数的建议

有符号数与无符号数之间的转换会经常地产生错误,并且无符号数在为0时若用它减去一个大于0的数,也会导致意想不到的结果。

比如下面这段程序:
length为a的长度

1
2
3
4
5
6
7
8
9
float sum_float(float a[], unsigned length)
{
int i;
float result = 0;
for(int i = 0; i < length - 1; i++){
result += a[i];
}
return result;
}

在传入的length为0时,不会返回0,但是却会报错,因为unsigned length在为0时,unsigned 0 - 1会得出一个Umax, 导致每个数都会符合for循环条件,便会导致访问数组中不存在的元素,导致出错。
一个可行的解决办法为将传入的了length改为int.

又或者这段比较字符串长度的代码:

1
2
3
4
size_t strlen(const char * s);
int strlonger(char *s, char * t) {
return strlen(s) - strlen(t) > 0;
}

看起来没有错误,但是注意size_t其实就是unsigned int.
因此当s为空字符串,且t不为空字符串时,就会发生与上文一致的错误。
一个可行的解决办吧是这样写strlonger函数:

1
2
3
int strlenger(char *s, char *t) {
return int(strlen(s)) - int(strlen(t));
}

就不会出现上述问题了。
因为无符号数经常出现各种错误,避免这类错误的最好的办法就是应该尽量避免无符号数的使用。

整数运算

本文地址: http://Humbertzhang.github.io/2017/12/20/CSAPP信息的表示与处理/