位运算 和 逻辑运算 以及 位运算指令
文章目录
- **1. 位运算(Bitwise Operations)**
- **(1)按位与(Bitwise AND)`&`**
- **(2)按位或(Bitwise OR)`|`**
- **(3)按位非(Bitwise NOT)`~`**
- **(4)按位异或(Bitwise XOR)`^`**
- **2. 逻辑运算(Logical Operations)**
- **(1)逻辑与(Logical AND)`&&`**
- **(2)逻辑或(Logical OR)`||`**
- **(3)逻辑非(Logical NOT)`!`**
- **3. 常见应用场景**
- **4. 完整示例**
- **总结**
- **为什么 `AND`操作会清零数据?**
- **1. `AND`指令的执行过程**
- **2. 汇编指令的“目标操作数”和“源操作数”**
- **3. 为什么 `XOR reg, reg`比 `AND reg, 0`更高效?**
- **4. 示例:不同清零方式的机器码对比**
- **5. 特殊情况:`SUB reg, reg`也能清零**
- **总结**
在 C 语言中,与(AND)、或(OR)、非(NOT)、异或(XOR) 是基本的位运算和逻辑运算,它们既可以对整数进行逐位运算,也可以用于布尔条件判断。
1. 位运算(Bitwise Operations)
位运算直接对整数的二进制位进行操作,适用于掩码(mask)、标志位、加密算法等场景。
(1)按位与(Bitwise AND)&
规则:两位同时为 1
,结果才为 1
,否则为 0
。
用途:屏蔽某些位(清零)、检查特定位是否设置。
示例:
#include <stdio.h>int main() {unsigned int a = 0b1100; // 12 (二进制 1100)unsigned int b = 0b1010; // 10 (二进制 1010)unsigned int result = a & b;printf("a & b = %u (二进制: %04b)\n", result, result);return 0;
}
输出:
a & b = 8 (二进制: 1000)
解释:
1100 (12)
& 1010 (10)
-----------1000 (8)
(2)按位或(Bitwise OR)|
规则:两位只要有一个 1
,结果就是 1
,否则为 0
。
用途:设置某些位为 1。
示例:
#include <stdio.h>int main() {unsigned int a = 0b1100; // 12unsigned int b = 0b1010; // 10unsigned int result = a | b;printf("a | b = %u (二进制: %04b)\n", result, result);return 0;
}
输出:
a | b = 14 (二进制: 1110)
解释:
1100 (12)
| 1010 (10)
-----------1110 (14)
(3)按位非(Bitwise NOT)~
规则:每一位取反(0
变 1
,1
变 0
)。
用途:反转所有位。
示例:
#include <stdio.h>int main() {unsigned int a = 0b1100; // 12unsigned int result = ~a;printf("~a = %u (二进制: %08b)\n", result, result);return 0;
}
输出(假设 unsigned int
是 8 位):
~a = 243 (二进制: 11110011)
解释:
~ 00001100 (12)
-----------11110011 (243)
(4)按位异或(Bitwise XOR)^
规则:两位不同时结果为 1
,相同时为 0
。
用途:交换变量、加密、校验。
示例:
#include <stdio.h>int main() {unsigned int a = 0b1100; // 12unsigned int b = 0b1010; // 10unsigned int result = a ^ b;printf("a ^ b = %u (二进制: %04b)\n", result, result);return 0;
}
输出:
a ^ b = 6 (二进制: 0110)
解释:
1100 (12)
^ 1010 (10)
-----------0110 (6)
2. 逻辑运算(Logical Operations)
逻辑运算用于条件判断,返回 1
(true
)或 0
(false
)。
(1)逻辑与(Logical AND)&&
规则:所有条件为真(非零)时,结果才为 1
,否则为 0
。
示例:
#include <stdio.h>int main() {int a = 5;int b = 0;int result = (a > 0) && (b > 0);printf("(a > 0) && (b > 0) = %d\n", result);return 0;
}
输出:
(a > 0) && (b > 0) = 0
(2)逻辑或(Logical OR)||
规则:只要有一个条件为真(非零),结果就是 1
,否则为 0
。
示例:
#include <stdio.h>int main() {int a = 5;int b = 0;int result = (a > 0) || (b > 0);printf("(a > 0) || (b > 0) = %d\n", result);return 0;
}
输出:
(a > 0) || (b > 0) = 1
(3)逻辑非(Logical NOT)!
规则:如果条件为假(0
),结果为 1
;否则为 0
。
示例:
#include <stdio.h>int main() {int a = 5;int result = !a;printf("!a = %d\n", result);return 0;
}
输出:
!a = 0
3. 常见应用场景
运算 | 示例 | 用途 |
---|---|---|
& | flags & MASK | 检查标志位是否设置 |
**` | `** | `flags |
~ | ~MASK | 反转掩码 |
^ | a ^= b; b ^= a; a ^= b; | 交换两个变量(无临时变量) |
&& | if (x > 0 && y > 0) | 逻辑条件判断 |
**` | `** | |
! | if (!error) | 检查条件是否为假 |
4. 完整示例
#include <stdio.h>int main() {// 位运算unsigned int a = 0b1100; // 12unsigned int b = 0b1010; // 10printf("a & b = %u\n", a & b); // 8printf("a | b = %u\n", a | b); // 14printf("~a = %u\n", ~a); // 4294967283(32 位取反)printf("a ^ b = %u\n", a ^ b); // 6// 逻辑运算int x = 5, y = 0;printf("x && y = %d\n", x && y); // 0printf("x || y = %d\n", x || y); // 1printf("!x = %d\n", !x); // 0printf("!y = %d\n", !y); // 1return 0;
}
总结
运算 | 符号 | 类型 | 用途 |
---|---|---|---|
与 | & | 位运算 | 屏蔽位、检查标志位 |
或 | ` | ` | 位运算 |
非 | ~ | 位运算 | 取反所有位 |
异或 | ^ | 位运算 | 交换变量、加密 |
逻辑与 | && | 逻辑运算 | 条件判断 |
逻辑或 | ` | ` | |
逻辑非 | ! | 逻辑运算 | 取反布尔值 |
希望这个示例能帮助你理解 C 语言中的与、或、非、异或运算! 🚀
为什么 AND
操作会清零数据?
是的,AND
操作会清零数据,是因为运算结果会写回(赋值给)目标操作数。在汇编语言中,大多数算术和逻辑运算(如 ADD
、SUB
、AND
、OR
、XOR
)都是 “破坏性” 的,即:
-
运算结果会覆盖目标操作数(通常是第一个操作数)。
-
源操作数(第二个操作数)保持不变。
1. AND
指令的执行过程
以 AND eax, 0
为例:
-
CPU 读取
eax
的当前值(假设是0x1234
)。 -
CPU 读取立即数
0
(二进制0000 0000
)。 -
CPU 对
eax
和0
进行逐位AND
运算:-
0 AND X = 0
(任何数与0
进行AND
,结果都是0
)。 -
因此,
eax
的每一位都会变成0
。
-
-
运算结果写回
eax
,所以eax
最终为0
。
2. 汇编指令的“目标操作数”和“源操作数”
在汇编语言中,大多数指令的格式是 OP 目标, 源
:
-
目标操作数(第一个操作数):会被修改,存放运算结果。
-
源操作数(第二个操作数):不会被修改,仅用于计算。
例如:
AND eax, 0 ; eax = eax & 0(eax 被修改,0 不变)
ADD ebx, ecx ; ebx = ebx + ecx(ebx 被修改,ecx 不变)
XOR edx, edx ; edx = edx ^ edx(edx 被修改)
3. 为什么 XOR reg, reg
比 AND reg, 0
更高效?
虽然 AND reg, 0
也能清零寄存器,但 XOR reg, reg
是更推荐的方式,原因:
方法 | 指令长度 | CPU 优化 | 适用场景 |
---|---|---|---|
AND reg, 0 | 需要编码 0 (可能占用更多字节) | 一般 | 需要清零时 |
XOR reg, reg | 更短(只需操作寄存器) | 高度优化(CPU 知道它是清零操作) | 推荐方式 |
MOV reg, 0 | 需要编码 0 (可能较长) | 一般 | 直接赋值 |
XOR reg, reg
的优势:
-
指令更短(不需要额外存储
0
)。 -
CPU 特殊优化(某些 CPU 会识别
XOR reg, reg
并直接清零,而不真正执行异或运算)。 -
通用性强(适用于所有 x86/x64 CPU)。
4. 示例:不同清零方式的机器码对比
我们对比 AND
、XOR
和 MOV
的机器码(x86-64):
指令 | 机器码(字节) | 说明 |
---|---|---|
AND eax, 0 | 83 E0 00 | 3 字节(83 E0 + 00 ) |
XOR eax, eax | 31 C0 | 2 字节(更短) |
MOV eax, 0 | B8 00 00 00 00 | 5 字节(B8 + 4 字节 0 ) |
显然,XOR eax, eax
是最短、最高效的方式。
5. 特殊情况:SUB reg, reg
也能清零
除了 AND
和 XOR
,SUB reg, reg
也能清零寄存器:
SUB eax, eax ; eax = eax - eax = 0
它的机器码是 29 C0
(2 字节),和 XOR
一样高效。
总结
问题 | 答案 |
---|---|
为什么 AND reg, 0 会清零? | 因为 AND 运算结果会写回目标操作数(第一个操作数)。 |
为什么 XOR reg, reg 比 AND reg, 0 更好? | XOR 指令更短,CPU 优化更好。 |
其他清零方式? | SUB reg, reg 、MOV reg, 0 (但 XOR 最优)。 |
所有逻辑/算术运算都会修改目标操作数吗? | 是的(如 ADD 、OR 、SUB 、XOR 都会修改目标操作数)。 |
如果你在写汇编代码,优先使用 XOR reg, reg
来清零寄存器!