《深入理解计算机系统》笔记 (1) 信息的位与表示

基础知识

:位(bit)是电子计算机中最小的数据单位。每一位的状态只能是0或1。

字节:1个字节(byte)由8个二进制位构成,是存储空间的基本计量单位,即最小的可寻址的存储器单位 ,。1个字节可以储存1个英文字母或者半个汉字,换句话说,1个汉字占据2个字节的存储空间。

虚拟存储器:也称为内存(memory),可视为一个非常大的字节数组,每个字节都由一个唯一的数字来标识,称为地址(address)。

:字(word)由若干个字节构成。在IA32中,一个字由2个字节构成。字是计算机进行数据处理和运算的单位。

字长:同一时间处理的二进制数位数叫字长。不同的机器有不同的字长,指明整数和指针数据的标称大小,并决定了虚拟地址空间的最大大小(对于一个字长为w位的机器而言,虚拟地址的范围为[0,\, 2^{w})。比如,一台8位机的字长为8位,虚拟地址的范围为[0,\, 2^{8});而在32位操作系统当中,字长为32位,虚拟地址的范围为[0,\, 2^{32})

C语言中,不同数据类型的字节数在不同的机器上是不同的,可以使用运算符sizeof(type)来确定对象使用的字节数。值得一提的是,指针的字节数完全取决于机器,因为指针中存储的是数据的地址,而地址字节数是由机器决定的,所以,在32位机上指针的大小为4个字节,而在64位机上则为8个字节。

《深入理解计算机系统》笔记 (1) 信息的位与表示_第1张图片 C语言中不同数据类型在不同机器下的字节数

 字节顺序

多字节对象在内存中是连续的序列,对象的地址为所用的字节中最小的地址。多字节对象的字节排序规则有两种,分别为小端法大端法。小端法即低位在低地址(最低有效字节在最前面),大端法则是高位在低地址(最高有效字节在最前面)。Intel的处理器采用的是小端法。

关于大小端,举个例子。我们要从0x100地址处存储值0x01234567,采用小端法的机器上排列的顺序为67 45 23 01,而大端法的机器上的顺序为01 23 45 67。

《深入理解计算机系统》笔记 (1) 信息的位与表示_第2张图片 小端法
《深入理解计算机系统》笔记 (1) 信息的位与表示_第3张图片 大端法

 一般而言,在不同的机器间传送的数据通常会导致问题。但比较特殊的是,字符串在任何系统上都将得到相同的结果。字符串是一个以'\0'(ASCII码为0)为结尾的字符数组,每个字符都以ASCII码来表示。如字符串"HNUer",在内存中的表示为48 4E 55 65 72 00。详见数据在内存中的存储方式。

         48('H') 4E('N') 55('U') 65('e') 72('r') 00('\0')         

 位级操作

  • 布尔代数

《深入理解计算机系统》笔记 (1) 信息的位与表示_第4张图片

  • C语言中的位级运算

  C语言支持按位布尔运算:&(与)、|(或)、~(非)、^(异或),这些布尔运算能运用到任何整型数据上(包括char)。

\sim 0x41 \rightarrow 0xBE(\sim 01000001_{2}\rightarrow 10111110_{2})

\sim 0x00 \rightarrow 0xFF(\sim 00000000_{2}\rightarrow 11111111_{2})

0x69\: \&\: 0x55 \rightarrow 0x41 (01101001_{2}\,\, \& \,\, 01010101_{2} \rightarrow 01000001_{2})

0x69\: |\: 0x55 \rightarrow 0x7D (01101001_{2}\,\, | \,\, 010101012_{2} \rightarrow 01111101_{2})

对位的布尔运算,有一些比较有趣的地方,比如对于任一位向量a,有a^{\wedge }a = 0(即对于每个位向量,其自身就是自己的一个加法逆元)。根据这个特性,可以编写一些看似奇技淫巧(实则没啥卵用)的小程序。比如这个:

void inplace_swap(int *x, int *y)
{
	*y = *x ^ *y;
	*x = *x ^ *y;
	*y = *x ^ *y;
}

void reverse_array(int a[], int cnt)
{
	int first, last;
	for(first = 0, last = cnt-1; first < last; ++first, --last)
		inplace_swap(&a[first], &a[last]);
}

位级运算的一个常见用法是实现掩码运算。比如,某个数x = 0x89ABCDEF,掩码mask = 0xFF,位级运算x&mask将生成一个由x的最低有效字节组成的值(0xEF),其他的字节就被设置为0,即达到了屏蔽某些位的作用。

  • C语言中的逻辑运算

C语言还支持逻辑运算:&&(与)、||(或)、!(非)。与按位布尔运算不同的是,逻辑运算只会返回0或1,并且,&&和||相对于&和|还有一个短路的特性,即如果对第一个参数求值就能确定表达式的结果,那么逻辑运算符就不会对第二个参数求值。

!0x41 \rightarrow 0x00

!!0x41 \rightarrow 0x01

0x69\: \&\&\: 0x55 \rightarrow 0x01

0x69\: ||\: 0x55 \rightarrow 0x01

  • C语言中的移位运算

C语言提供了一组移位运算:左移(<<)和右移(>>)。

对于一个位向量x,C表达式x << k使得x左移k位,丢弃最高的k位,并在右端补k个0。具体来说,对位向量01100010,01100010 << 3的结果为00010000

而右移稍微有些不同,出于对符号的考虑,有两种形式的右移:逻辑右移与算术右移。对于x >> k,逻辑右移在左端补k个0,而算术右移在左端补k个最高有效位的值。算术右移对有符号整数数据的运算非常有用,它在右移时不会发生符号的变化。举个例子,对于位向量10100010,逻辑右移2位的结果是00101000,而算术右移2位的结果是11101000。一般而言,对于无符号数据,右移必须是逻辑的;对于有符号数据,右移一般是算术的。

当移位的位数大于数本身的位数时(即w位的数据移动k位,而k ≥ w),C语言并没有说明这种情况下该如何做,但在许多机器上,采取的是只考虑位移量的低log_{2}w位,即实际的位移量只有这么多。

还有一点就是操作符的优先度问题。加减法的优先度是高于移位运算的,1 <<2+3<<4计算的实际上是1 <<(2+3)<<4,而不是(1 <<2)+(3<<4)。所以,务必要记得给移位运算加上括号。

你可能感兴趣的:(计算机系统)