~是逐位取反,对整数所有二进制位0变1、1变0;!是逻辑非,非零值为真则结果为0,零值为假则结果为1。

~ 是逐位翻转,! 是真假判断
~ 操作的对象是整数在内存里的二进制位,不管这个数是正、负还是零,它都老老实实把每一位 0 变 1、1 变 0。比如 int a = 5;(32 位补码:0000…0101),~a 就是 1111…1010。这个结果不是“5 的相反数”,而是对全部 32 个位做镜像翻转。
! 操作的对象是“值的逻辑意义”:只要数值非零,就视为真,! 后得 0;只有数值为 0,才视为假,! 后得 1。它不看二进制长什么样,只问一句:“这数算不算‘有’?” 算,就返 0;不算(即等于 0),就返 1。
补码环境下 ~ 的结果怎么算
在有符号整型(如 int)中,~ 的结果必须结合补码规则理解:
- 对正数 x,~x 等价于 -(x + 1)。例如 ~5 = -6,~0 = -1,~100 = -101
- 对负数 y(比如 -3,补码是 1111…1101),~y 等价于 |y| - 1。例如 ~(-3) = 2,~(-1) = 0
- 对 0:~0 在有符号 int 中就是全 1,即 0xFFFFFFFF,补码解释为 -1
关键点:~ 不改变数据类型,也不做类型转换;它直接操作存储位,结果解释方式取决于变量类型(有符号/无符号)。
典型使用场景对比
用 ~ 的时候,你在“动比特”:
- 置位/清位掩码:比如 flags & ~BIT2 清除第 2 位
- 生成掩码常量:~0U 得到全 1 的无符号数(如 0xFFFFFFFF)
- 嵌入式寄存器操作:直接翻转某组控制位,如 LED 状态反转
- 与 &、| 配合做位域控制,例如 reg = (reg & ~0x0F) | (new_val & 0x0F)
用 ! 的时候,你在“做决策”:
- 条件判断:if (!ptr) { /* 指针为空 */ }
- 状态标志取反:while (!done) { … }
- 简化布尔表达式:!is_valid() 替代 is_valid() == 0
- 注意:! 对浮点数、指针、枚举等也适用,只要能隐式转为“零/非零”
一个容易踩坑的实例
假设 uint8_t status = 0x01;(仅 bit0 置位):
- !status → 0(因为 0x01 ≠ 0,逻辑真,取反为假 → 0)
- ~status(作为 uint8_t)→ 0xFE(二进制 11111110)
- 但如果把它当 int 打印:printf("%d", ~status);,会先提升为 int 再取反,结果是 -2(因有符号解释)
所以混用时务必确认:你是在处理“位模式”,还是在检查“是否为零”。选错运算符,轻则逻辑错乱,重则硬件误动作。

















