这篇文章最后更新于 some 天前,内容可能已经过时。
逻辑运算 在之前的关系表达式中,只能描述单一条件,那如何描述a条件同时b条件之类的呢?此处就要用到逻辑表达式。
逻辑运算符 种类
符号
名称
功能描述
示例
&&
逻辑与
当两个操作数都为真时,结果才为真;否则为假
int a = 5, b = 10; if(a > 0 && b > 0)
||
逻辑或
当两个操作数中至少一个为真时,结果为真;都为假时才为假
int x = 3, y = -1; if(x != 0 || y != 0)
!
逻辑非(取反)
单目运算符,将其操作数的真假值取反:真变假,假变真
int flag = 0; if(!flag)
💡 在C语言中,任何非0值 都被视为“真”(true),而只有 0 被视为“假”(false)。
关于if(a),此处的判断条件是a是否是非0值。
逻辑运算真值表
a
b
!a
!b
a && b
a || b
假
假
真
真
假
假
假
真
真
假
假
真
真
假
假
真
假
真
真
真
假
假
真
真
逻辑运算符的优先级 在C语言中,!、&&,||的优先级依次降低。
示例:优先级的影响 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int a = 1 , b = 0 ;if (!a && b) { } int x = 1 , y = 0 ;if (x && y || 1 ) { } int m = 0 , n = 1 ;if (m || n && !m) { }
逻辑运算符
优先级
示例说明
!
最高
!a && b 先计算 \!a 再计算 &&
&&
其次
a && b || c 先计算 && 再计算 ||
||
最低
a || b && c 先计算 b && c 再计算 ||
逻辑运算符的结合方向 示例:结合方向的影响 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int flag = 0 ;if (!flag && 5 ) { } int a = 1 , b = 1 , c = 0 ;if (a && b && c) { } int x = 0 , y = 1 , z = 0 ;if (x || y || z) { }
逻辑运算符
结合方向
示例说明
!
从右向左
!flag && x 等价于 (!flag) && x
&&
从左向右
a && b && c 等价于 (a && b) && c
||
从左向右
x || y || z 等价于 (x || y) || z
短路特性: 短路特性(Short-circuit Evaluation)是逻辑运算符(&& 和 ||)在C语言中的行为规则: 当逻辑运算的结果已经可以确定时,跳过后续操作数的计算 。这种机制提高了程序效率,并能避免潜在错误。
1. 短路特性的工作原理 (1) 逻辑与(&&)
规则 :
如果第一个操作数为假(0) ,整个表达式的结果必为假,跳过第二个操作数的计算 。
真值表 :
a
b
a && b
0
任意
0
1
0
0
1
1
1
(2) 逻辑或(||)
规则 :
如果第一个操作数为真(非0) ,整个表达式的结果必为真,跳过第二个操作数的计算 。
真值表 : | a | b | a || b | |——|——|——–| | 1 | 任意 | 1 | | 0 | 0 | 0 | | 0 | 1 | 1 |
2. 短路特性的应用场景 (1) 避免运行时错误 空指针检查 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <stdio.h> void process (int *ptr) { if (ptr && *ptr > 0 ) { printf ("值为 %d\n" , *ptr); } else { printf ("指针为空或值无效\n" ); } } int main () { int *p = NULL ; process(p); return 0 ; }
原理 :ptr && *ptr > 0 中,如果 ptr 为 NULL,&& 会短路,跳过 *ptr 的解引用,避免崩溃。
避免除零 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <stdio.h> int main () { int a = 5 ; int b = 0 ; if (b != 0 && a / b > 1 ) { printf ("结果有效\n" ); } else { printf ("除数不能为 0\n" ); } return 0 ; }
(2) 优化性能 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <stdio.h> #include <time.h> int expensive_check () { for (int i = 0 ; i < 100000000 ; ++i); return 0 ; } int main () { clock_t start = clock(); if (0 && expensive_check()) { } double duration = (double )(clock() - start) / CLOCKS_PER_SEC; printf ("耗时: %.6f 秒\n" , duration); return 0 ; }
(3) 默认值赋值 1 2 3 4 5 6 7 8 #include <stdio.h> int main () { int *ptr = NULL ; int value = (ptr != NULL ) ? *ptr : 0 ; printf ("value = %d\n" , value); return 0 ; }
4. 短路特性的注意事项 (1) 条件顺序的重要性
逻辑与(&&) :将更可能为 假 的条件放在左侧。
1 if (is_valid && is_ready) { ... }
逻辑或(||) :将更可能为 真 的条件放在左侧。
1 if (is_cached || fetch_from_database()) { ... }
(2) 副作用的影响 1 2 3 int i = 0 ;int result = (i++ > 0 ) && (i++ > 1 ); printf ("%d" , i);
解释 :i++ > 0 为假,i++ > 1 不会被执行,因此 i 只自增一次。
(3) 优先级问题 1 if (a || b && c) { ... }
建议 :使用括号明确优先级:1 if ((a || b) && c) { ... }
5. C语言与其他语言的差异
语言
逻辑与
逻辑或
特性说明
C
&&
`
Python
and
or
返回实际值(非布尔值)
JavaScript
&&
`
C++
&&
`
6. 总结 优点
提高性能 :跳过不必要的计算。
防止错误 :避免空指针、除零等运行时异常。
简化代码 :如默认值赋值(value = a || b)。
潜在问题
副作用 :依赖短路跳过的表达式可能导致逻辑错误。
可读性 :复杂条件可能导致代码难以理解。
最佳实践
显式括号 :明确逻辑运算的优先级。
避免副作用 :不依赖短路跳过的表达式中的副作用。
条件顺序优化 :将更可能触发短路的条件放在左侧。
位运算符 按位异或运算符 运算符种类
符号
名称
功能描述
示例
&
按位与
对两个操作数的每一位执行逻辑000与操作
int a = 5 & 3; // 1
|
按位或
对两个操作数的每一位执行逻辑或操作
int b = 5 | 3; // 7
^
按位异或
当两个操作数的对应位不同时为1,相同时为0
int c = 5 ^ 3; // 6
~
按位取反
单目运算符,将操作数的每一位取反
int d = ~5; // 依赖于系统位数
<<
左移
将操作数的二进制位向左移动指定的位数
int e = 5 << 1; // 10
>>
右移
将操作数的二进制位向右移动指定的位数
int f = 5 >> 1; // 2
按位异或特性 1 2 3 4 5 // 交换两个变量的值(不使用临时变量) int a = 10, b = 20; a = a ^ b; // a becomes 26 (11010) b = a ^ b; // b becomes 10 (1010) a = a ^ b; // a becomes 20 (10100)
典型应用场景
数据加密 :使用异或进行简单加密
1 2 3 4 5 char data = 'A'; char key = 'K'; data = data ^ key; // 加密 // 重复异或操作可解密 data = data ^ key; // 恢复为'A'
位掩码操作
1 2 3 4 // 提取第3位和第1位(从0开始计数) int flags = 0b101010; int mask = 0b1010; // 二进制掩码 int result = flags & mask; // 保留指定位置
注意事项
逻辑运算 vs 位运算 :
&& 和 || 返回布尔值(0或1)
& 和 | 返回整数值
移位陷阱 :1 2 int x = -1 ;int y = x >> 1 ;
结合性问题 :