之前学习c语言的时候课程中并没有介绍过位操作,关于位操作也是之后零零散散地遇到。最近又遇到了一个问题,索性就总结一下吧。
问题大概是这样的:
unsigned char x = 0xf4;
printf( "%x",~x );
输出是什么??
之前以为是 b。因为16进制 f4 = 1111,0100,取反之后是 0000,1011,即是b。但是结果却是ffffff0b。
其实这还涉及到一个“Integer Promotion”的问题:在一个表达式中,凡是可以使用int或unsigned int类型做右值的地方也都可以使用有符号或无符号的char型、short型和Bit-field。如果原始类型的取值范围都能用int型表示,则其值被提升为int型,如果表示不了就提升为unsigned int型。
在这里,可以理解实际上有一个整型y(16进制)
y = ~x
进行计算时,首先需要把字符型的x转成整型,此时x实际为:0x000000f4。然后再进行取反运算,达到:0xffffff0b
还有一个比较容易出错的问题就是有符号数的移位了,比如:
int i = 0xcffffff3;
printf("%x\n",0xcffffff3 >> 2);
printf("%x\n",i >> 2);
得到的竟是两个不一样的结果:33fffffc和f3fffffc
仔细分析其实不难理解。第一句的输出是正常的整数移位,按正常的步骤就能得到结果。而第二句是整型的移位,是有符号位的。即i的存储是:
1100,1111,...,0011
可以看出最高位,即符号位是1,说明是负数。所以编译器在进行移位操作的时候是补1而不是补0,所以向右移两位后,是:
1111,0011,1111,...,1100
此外,还有一个需要注意的问题就是:移动的位数必须小于左操作数的总位数。
例如:
int i = 0xcffffff3;
printf("%x\n",i >> 33);
此时,虽然也能得到结果:e7fffff9,但是却会报一个warning:
说到位运算,其实有很多性质,我们可以巧妙地利用,从而带来一定程度上的优化。最典型的就是不借助额外的存储空间来进行两个整数的交换:
a = a ^ b;
b = b ^ a;
a = a ^ b;
还有就是利用掩码来对特定的位进行提取、置零、置一的操作,这些可以对一些位图进行简单的处理操作。