1.位运算
整数在计算机中用二进制的位来表示,C语言提供一些运算符可以直接操作整数中的位,称为位运算,位运算符的操作数都必须是整型的
。
- 按位与(Bitwise AND)运算符&
- 按位或(Bitwise OR)运算符|
- 按位取反(Bitwise NOT)运算符~
- 按位异或(Bitwise XOR)运算符^
在一定的取值范围内,将一个整数左移1位相当于乘以2。比如二进制11(十进制3)左移一位变成110,就是6,再左移一位变成1100,就是12。读者可以自己验证这条规律对有符号数和无符号数都成立,对负数也成立。当然,如果左移改变了最高位(符号位),那么结果肯定不是乘以2了,所以我加了个前提“在一定的取值范围内”。
综上所述,由于类型转换和移位等问题,用有符号数做位运算是很不方便的,所以,建议只对无符号数做位运算(或者有符号数的非负数),以减少出错的可能。
2.掩码
掩码0x0000ff00表示对一个32位整数的8~15位进行操作、
1、取出8~15位。
unsigned int a, b, mask = 0x0000ff00;
a = 0x12345678;
b = (a & mask) >> 8; /* 0x00000056 */
2、将8~15位清0。
3、将8~15位置1。
3.异或运算的一些特性
如果a1 ^ a2 ^ a3 ^ … ^ an的结果是1,则表示a1、a2、a3…an之中1的个数为奇数个,否则为偶数个。这条性质可用于奇偶校验(Parity Check),比如在串口通信过程中,每个字节的数据都计算一个校验位,数据和校验位一起发送出去,这样接收方可以根据校验位粗略地判断接收到的数据是否有误。
x ^ x ^ y == y,因为x ^ x == 0,0 ^ y == y。这个性质有什么用呢?我们来看这样一个问题:交换两个变量的值,不得借助额外的存储空间,所以就不能采用temp = a; a = b; b = temp;的办法了。利用位运算可以这样做交换:
a = a ^ b;
b = b ^ a;
a = a ^ b;
4.复合赋值运算符
复合赋值运算符(Compound Assignment Operator)包括*= /= %= += -= <<= >>= &= ^= |=,一边做运算一边赋值。
例如a += 1相当于a = a + 1。但有一点细微的差别,前者对表达式a只求值一次,而后者求值两次,如果a是一个复杂的表达式,求值一次和求值两次的效率是不同的,例如a[i+j] += 1和a[i+j] = a[i+j] + 1。
对于没有Side Effect的表达式,求值一次和求值两次的结果是一样的,但对于有Side Effect的表达式则不一定
,例如a[foo()] += 1和a[foo()] = a[foo()] + 1,如果foo()函数调用有Side Effect,比如会打印一条消息,那么前者只打印一次,而后者打印两次。
5.sizeof运算符与typedef类型声明
sizeof 表达式”和“sizeof(类型名)
。
int a[12];
printf("%d\n", sizeof a/sizeof a[0]);
sizeof(数组名)的时候,数组名不做左值也不做右值,纯粹就是数组名的含义。那么sizeof(数组名)实际返回的是整个数组所占用内存空间(以字节为单位的)。
typedef这个关键字用于给某种类型起个新名字
typedef char array_t[10];
array_t a;
这相当于声明char a[10]