在 C语言中,位运算符是一组用于执行二进制数(通常表示为整数)的位级操作的运算符。这些运算符直接操作整数的二进制位,因此通常具有非常高的速度和效率。
位运算符能够深入操作数的内部,并根据非 0 值或 0 值将二进制位视为真或假,然后进行逻辑运算。
以下是 C语言中的位运算符:
位逻辑与 &;
位逻辑或 |;
位逻辑异或 ^;
位逻辑非 ~。
为了更好地理解这些位逻辑运算符的特性,我们选取两个整数 170 和 102,并使用自定义的 printBinary() 函数输出它们的二进制表示形式。
void printBinary(unsigned char dec)
{
// 数组初始化为 0
char bits[8] = {0};
int count = 0;
int quotient;
int remainder;
while(dec > 0)
{
remainder = dec % 2;
quotient = dec / 2;
dec = quotient;
bits[count] = remainder;
count++;
}
// 逆序输出所有二进制位
for (int i = 8 - 1; i >= 0; i--)
printf("%d", bits[i]);
putchar('\n');
}
printBinary(170);
printBinary(102);
将十进制数 170 作为 printBinary() 函数的参数,它将输出 170 对应的二进制数 10101010。将十进制数 102 作为 printBinary() 函数的参数,它将输出 102 对应的二进制数 01100110。
接下来,我们对这两个数值分别进行各种位逻辑运算,以观察其运算结果。
C语言位逻辑与&
尝试对 170 和 102 进行位逻辑与运算:
printf("%hhu\n", 170 & 102);
printBinary(170 & 102);
运行结果为:
34
00100010
表达式 170&102 的结果为十进制 34,二进制 00100010。让我们来分析为何得到这个结果。
位逻辑与 & 操作会深入字节内部,并对二进制位进行逻辑与运算。如果两个位同时为真(1),则运算结果为真(1);否则,运算结果为假(0)。
查看下图,从左至右两个操作数的第 1 个二进制位分别为 1 和 0。1 & 0 结果为假,因此得到结果 0。
图 1 1&0
查看下图,从左至右两个操作数的第 2 个二进制位分别为 0 和 1。0 & 1 结果为假,因此得到结果 0。
图 2 0&1
查看下图,从左至右两个操作数的第 3 个二进制位分别为 1 和 1。1 & 1 结果为真,因此得到结果 1。
图 3 1&1
查看下图,从左至右两个操作数的第 4 个二进制位分别为 0 和 0。0 & 0 结果为假,因此得到结果 0。
图 4 0&0
以此类推,后面 4 个二进制位的运算与前面一致,最终的结果为 00100010。
接下来,将最终的二进制结果 00100010 转换为十进制,从最高位开始:
第 1 位权值为 2^7,该位为 0,积为 0。
第 2 位权值为 2^6,该位为 0,积为 0。
第 3 位权值为 2^5,该位为 1,积为 2^5。
第 4 位权值为 2^4,该位为 0,积为 0。
第 5 位权值为 2^3,该位为 0,积为 0。
第 6 位权值为 2^2,该位为 0,积为 0。
第 7 位权值为 2^1,该位为 1,积为 2^1。
第 8 位权值为 2^0,该位为 0,积为 0。
将所有乘积累加起来,得到其十进制表示:
2^5 + 2^1 = 32 + 2 = 34
因此,位逻辑与运算 170 & 102 得到的十进制结果为 34,二进制结果为 00100010。
C语言位逻辑或|
尝试对 170 和 102 进行位逻辑或运算:
printf("%hhu\n", 170 | 102);
printBinary(170 | 102);
运行结果如下图所示:
238
11101110
表达式 170 | 102 的十进制结果为 238,二进制结果为 11101110。接下来分析为什么会得到这个结果。
位逻辑或 | 运算将深入字节内部,对二进制位进行逻辑或运算。如果两个位同时为假(0),则运算结果为假(0);否则,运算结果为真(1)。
查看下图,从左至右两个操作数的第 1 个二进制位分别为 1 和 0。1 | 0 结果为真,因此得到结果 1。
图 5 1|0
查看下图,从左至右两个操作数的第 2 个二进制位分别为 0 和 1。0 | 1 结果为真,因此得到结果 1。
图 6 0|1
查看下图,从左至右两个操作数的第 3 个二进制位分别为 1 和 1。1 | 1 结果为真,因此得到结果 1。
图 7 1|1
查看下图,从左至右两个操作数的第 4 个二进制位分别为 0 和 0。0 | 0 结果为假,因此得到结果 0。
图 8 0|0
以此类推,后面 4 个二进制位的运算与前面一致,最终的结果为 11101110。
接下来,将最终的二进制结果 11101110 转换为十进制,从最高位开始:
第 1 位权值为 2^7,该位为 1,积为 2^7。
第 2 位权值为 2^6,该位为 1,积为 2^6。
第 3 位权值为 2^5,该位为 1,积为 2^5。
第 4 位权值为 2^4,该位为 0,积为 0。
第 5 位权值为 2^3,该位为 1,积为 2^3。
第 6 位权值为 2^2,该位为 1,积为 2^2。
第 7 位权值为 2^1,该位为 1,积为 2^1。
第 8 位权值为 2^0,该位为 0,积为 0。
将所有乘积累加起来,得到其十进制表示:
2^7 +2^6 + 2^5 +2^3 + 2^2 + 2^1 = 128 + 64 + 32 + 8 + 4 + 2 = 238
因此,位逻辑或运算 170 | 102 得到的十进制结果为 238,二进制结果为 11101110。
C语言位逻辑异或^
尝试对 170 和 102 进行位逻辑异或运算。
printf("%hhu\n", 170 ^ 102);
printBinary(170 ^ 102);
运行结果为:
204
11001100
表达式 170 ^ 102 的十进制结果为 204,二进制结果为 11001100。接下来分析为什么会得到这个结果。
位逻辑异或 ^ 运算将深入字节内部,对二进制位进行逻辑异或运算。若两个位不同时,运算结果为真(1);否则,运算结果为假(0)。
查看下图,从左至右两个操作数的第 1 个二进制位分别为 1 和 0。1 ^ 0,两个位不同,结果为真,因此得到结果 1。
图 9 1^0
查看下图,从左至右两个操作数的第 2 个二进制位分别为 0 和 1。0 ^ 1,两个位不同,结果为真,因此得到结果 1。
图 10 0^1
查看下图,从左至右两个操作数的第 3 个二进制位分别为 1 和 1。1 | 1,两个位相同,结果为假,因此得到结果 0。
图 11 1^1
查看下图,从左至右两个操作数的第 4 个二进制位分别为 0 和 0。0 | 0,两个位相同,结果为假,因此得到结果 0。
图 12 0^0
以此类推,后面 4 个二进制位的运算与前面一致,最终的结果为 11001100。
接下来,将最终的二进制结果 11001100 转换为十进制,从最高位开始:
第 1 位权值为 2^7,该位为 1,积为 2^7。
第 2 位权值为 2^6,该位为 1,积为 2^6。
第 3 位权值为 2^5,该位为 0,积为 0。
第 4 位权值为 2^4,该位为 0,积为 0。
第 5 位权值为 2^3,该位为 1,积为 2^3。
第 6 位权值为 2^2,该位为 1,积为 2^2。
第 7 位权值为 2^1,该位为 0,积为 0。
第 8 位权值为 2^0,该位为 0,积为 0。
将所有乘积累加起来,得到其十进制表示。
2^7 + 2^6 + 2^3 + 2^2 = 128 + 64 + 8 + 4 = 204
因此,位逻辑异或运算 170 ^ 102 得到的十进制结果为 204,二进制结果为 11001100。
C语言位逻辑非~
前面介绍了几个双目位逻辑运算符,它们会对运算符左右两边的运算对象进行计算,并得到一个结果。
接下来将介绍的位逻辑非是一个单目运算符,它仅对其右侧的运算对象进行计算。现在,让我们尝试对 170 和 102 进行位逻辑非运算。
printf("%hhu\n", ~170);
printBinary(~170);
printf("%hhu\n", ~102);
printBinary(~102);
运行结果为:
85
01010101
153
10011001
表达式 ~170 的结果是十进制数 85,二进制数 01010101。表达式 102 的结果是十进制数 153,二进制数 10011001。接下来,我们来分析为什么会得到这样的结果。
位逻辑非 ~ 会深入字节内部,并对二进制位执行逻辑非运算。如果二进制位为真(1)时,则运算结果为假(0);如果二进制位为假(0)时,则运算结果为真(1)。
换句话说,位逻辑非运算会翻转运算对象的所有二进制位。查看下图,二进制位 1 变为 0,二进制位 0 变为 1。
图 13 翻转二进制位
接下来,将 ~170 最终的二进制结果 01010101 转换为十进制,从最高位开始:
第 1 位权值为 2^7,该位为 0,积为 0。
第 2 位权值为 2^6,该位为 1,积为 2^6。
第 3 位权值为 2^5,该位为 0,积为 0。
第 4 位权值为 2^4,该位为 1,积为 2^4。
第 5 位权值为 2^3,该位为 0,积为 0。
第 6 位权值为 2^2,该位为 1,积为 2^2。
第 7 位权值为 2^1,该位为 0,积为 0。
第 8 位权值为 2^0,该位为 1,积为 2^0。
将所有乘积累加起来,得到其十进制表示:
2^6 + 2^4 + 2^2+ 2^0 = 64 + 16 + 4 + 1 = 85
因此,位逻辑非运算 ~170 得到的十进制结果为 85,二进制结果为 01010101。
接下来将 ~102 最终的二进制结果 10011001 转换为十进制,从最高位开始:
第 1 位权值为 2^7,该位为 1,积为 2^7。
第 2 位权值为 2^6,该位为 0,积为 0。
第 3 位权值为 2^5,该位为 0,积为 0。
第 4 位权值为 2^4,该位为 1,积为 2^4。
第 5 位权值为 2^3,该位为 1,积为 2^3。
第 6 位权值为 2^2,该位为 0,积为 0。
第 7 位权值为 2^1,该位为 0,积为 0。
第 8 位权值为 2^0,该位为 1,积为 2^0。
将所有乘积累加起来,得到其十进制表示:
2^7 + 2^4 + 2^3 + 2^0 = 128 + 16 + 8 + 1 = 153
因此,位逻辑非运算 ~102 得到的十进制结果为 153,二进制结果为 10011001。