位:位(bit)是电子计算机中最小的数据单位。每一位的状态只能是0或1。
字节:1个字节(byte)由8个二进制位构成,是存储空间的基本计量单位,即最小的可寻址的存储器单位 ,。1个字节可以储存1个英文字母或者半个汉字,换句话说,1个汉字占据2个字节的存储空间。
虚拟存储器:也称为内存(memory),可视为一个非常大的字节数组,每个字节都由一个唯一的数字来标识,称为地址(address)。
字:字(word)由若干个字节构成。在IA32中,一个字由2个字节构成。字是计算机进行数据处理和运算的单位。
字长:同一时间处理的二进制数位数叫字长。不同的机器有不同的字长,指明整数和指针数据的标称大小,并决定了虚拟地址空间的最大大小(对于一个字长为w位的机器而言,虚拟地址的范围为。比如,一台8位机的字长为8位,虚拟地址的范围为
;而在32位操作系统当中,字长为32位,虚拟地址的范围为
。
C语言中,不同数据类型的字节数在不同的机器上是不同的,可以使用运算符sizeof(type)来确定对象使用的字节数。值得一提的是,指针的字节数完全取决于机器,因为指针中存储的是数据的地址,而地址字节数是由机器决定的,所以,在32位机上指针的大小为4个字节,而在64位机上则为8个字节。
多字节对象在内存中是连续的序列,对象的地址为所用的字节中最小的地址。多字节对象的字节排序规则有两种,分别为小端法与大端法。小端法即低位在低地址(最低有效字节在最前面),大端法则是高位在低地址(最高有效字节在最前面)。Intel的处理器采用的是小端法。
关于大小端,举个例子。我们要从0x100地址处存储值0x01234567,采用小端法的机器上排列的顺序为67 45 23 01,而大端法的机器上的顺序为01 23 45 67。
一般而言,在不同的机器间传送的数据通常会导致问题。但比较特殊的是,字符串在任何系统上都将得到相同的结果。字符串是一个以'\0'(ASCII码为0)为结尾的字符数组,每个字符都以ASCII码来表示。如字符串"HNUer",在内存中的表示为48 4E 55 65 72 00。详见数据在内存中的存储方式。
48('H') | 4E('N') | 55('U') | 65('e') | 72('r') | 00('\0') |
C语言支持按位布尔运算:&(与)、|(或)、~(非)、^(异或),这些布尔运算能运用到任何整型数据上(包括char)。
对位的布尔运算,有一些比较有趣的地方,比如对于任一位向量a,有(即对于每个位向量,其自身就是自己的一个加法逆元)。根据这个特性,可以编写一些看似奇技淫巧(实则没啥卵用)的小程序。比如这个:
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语言还支持逻辑运算:&&(与)、||(或)、!(非)。与按位布尔运算不同的是,逻辑运算只会返回0或1,并且,&&和||相对于&和|还有一个短路的特性,即如果对第一个参数求值就能确定表达式的结果,那么逻辑运算符就不会对第二个参数求值。
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语言并没有说明这种情况下该如何做,但在许多机器上,采取的是只考虑位移量的低位,即实际的位移量只有这么多。
还有一点就是操作符的优先度问题。加减法的优先度是高于移位运算的,计算的实际上是
,而不是
。所以,务必要记得给移位运算加上括号。