表达式(十四)
十、位运算符
C语言中位运算符(bitwise operators)包括:按位与运算符(&)、按位或运算符(|)、按位异或运算符(^)、按位取反运算符(~)、左移运算符(<<)、右移运算符(>>)。位运算符的操作数应为整数类型,操作数会执行常用算术转换(usual arithmetic conversions)。位运算符的运算结果取决于整数的内部表示;并且对于有符号整数存在实现定义方面和未定义方面。
位运算符表达式的语法格式分别如下所示:
expression & expression
expression | expression
expression ^ expression
~ expression
expression << expression
expression >> expression
其中expression为整型表达式。
1、按位与运算符
按位与运算符(&)的运算规则:
1 & 1 = 1
1 & 0 = 0
0 & 1 = 0
0 & 0 = 0
按位与运算符(&)的运算结果是左操作数和右操作数的按位与运算结果,即当且仅当转换后的操作数中对应位都设置时,结果中的位将被设置;结果类型是常用算术转换规则确定的类型。
signed char a = 5; signed char b = 7; a&b; //表达式a&b的值为5,类型为int类型。
2、按位或运算符
按位或运算符(|)的运算规则:
1 | 1 = 1
1 | 0 = 1
0 | 1 = 1
0 | 0 = 0
按位或运算符(|)的运算结果是左操作数和右操作数的按位或运算结果,即当且仅当转换后的操作数中至少一个对应位被设置时,结果中的位将被设置;结果类型是常用算术转换规则确定的类型。
signed char a = 5; signed char b = 7; a|b; //表达式a|b的值为7,类型为int类型。
3、按位异或运算符
按位异或运算符(^)的运算规则:
1 ^ 1 = 0
1 ^ 0 = 1
0 ^ 1 = 1
0 ^ 0 = 0
按位异或运算符(^)的运算结果是左操作数和右操作数的按位异或运算结果,即当且仅当转换后的操作数中只有一个对应位被设置时,结果中的位将被设置;结果类型是常用算术转换规则确定的类型。
signed char a = 5; signed char b = 7; a^b; //表达式a^b的值为2,类型为int类型。
4、按位取反运算符
按位取反运算符(~)的运算规则:
~ 1 = 0
~ 0 = 1
按位取反运算符(~)的运算结果是操作数所有位取反,即当且仅当转换后的操作数中对应位未被设置时,结果中的位将被设置;结果类型是常用算术转换规则确定的类型。如果常用算术转换规则确定的结果类型是无符号整数类型,表达式~E等价于结果类型的最大可表示值减E,以结果类型为unsigned int类型为例,表达式~E等价于表达式UINT_MAX - E。
#define TYPE(x) _Generic((x), \ signed char : "signed char type", \ int : "int type", \ default : "unknown type" \ ) ... signed char ch = 1; unsigned char uch = 1; printf("%d\n", ~ch); //输出-2。 printf("%s\n", TYPE(~ch)); //输出int type。 printf("%u\n", ~uch); //输出4294967294。 printf("%u\n", UINT_MAX-uch); //输出4294967294。
以int类型对象占用4个字节为例,int类型整数1的存储形式为0000 0000 0000 0000 0000 0000 0000 0001。位取反得到的数值存储形式为1111 1111 1111 1111 1111 1111 1111 1110。对于有符号整数,最高位是符号位,其余位是数值位,所以值为-2。对于无符号整数,所有位都是数值位,所以值为4294967294,即UINT_MAX - 1。
5、移位运算符
移位运算符包括左移运算符(<<)和右移运算符(>>),移位运算符的结果类型是提升后的左操作数的类型。
signed char sc = 1; unsigned char uc = 64u; sc << 2; //表达式sc << 2的类型是int类型。 uc >> 2; //表达式uc >> 2的类型是int类型。
如果右操作数的值是负值或者大于等于提升后的左操作数的宽度,其行为是未定义的。
int i = 64; i << -2; //未定义行为,右操作数是负数。 i << CHAR_BIT*sizeof(int); //未定义行为,右操作数等于左操作数的宽度。
E1 << E2的结果是E1左移E2个位;空出的位填充0。如果E1是无符号整数类型,并且结果值在结果类型可表示的值域范围内,结果值为E1×2E2,否则结果值为E1×2E2持续减模(模等于结果类型可表示的最大值加1。),直至结果值在结果类型可表示的值域范围内。如果E1是有符号整数类型的非负值,并且E1×2E2在结果类型可表示的值域范围内,E1×2E2就是结果值;否则其行为是未定义的。
unsigned ui1 = 0x0000000F; unsigned ui2 = 0xF0000000; signed i1 = 0x0000000F; signed i2 = 0x70000000; printf("%010#X\n", ui1 << 2); //输出0X0000003C,即60。 printf("%010#X\n", ui2 << 2); //输出0XC0000000。 /*UINT_MAX+1是unsigned类型对应的模。*/ printf("%010#X\n", 4*ui2-2*(UINT_MAX+1)); //输出0XC0000000。 printf("%d\n", i1 << 2); //输出60。 printf("%d\n", i2 << 2); //未定义行为。
E1 >> E2的结果是E1右移E2个位。如果E1是无符号整数类型或者E1是有符号整数类型的非负值,结果值为E1/2E2的商的整数部分。如果E1是有符号整数类型的负值,则结果值由实现定义。
unsigned ui = 0x0000000F; signed i1 = 0x0000000F; signed i2 = 0xF0000000; printf("%010#X\n", ui >> 2); //输出0X00000003,即3。 printf("%d\n", i1 >> 2); //输出3。 printf("%d\n", i2 >> 2); //实现定义行为。