逻辑运算符
逻辑运算符 &&(逻辑与)、||(逻辑或)、!(逻辑非)用于组合和取反条件表达式。它们的结果为 1(真)或 0(假),且 && 和 || 具有短路求值特性——左侧表达式的结果能确定整体结果时,右侧表达式不会执行。这一特性既是性能优化手段,也是安全编程的重要工具。
逻辑与 &&
&& 要求两侧表达式都为真(非零),结果才为真:
int a = 5, b = 10;
printf("%d\n", a > 0 && b > 0); /* 1:两者都为真 */
printf("%d\n", a > 0 && b < 0); /* 0:右侧为假 */
printf("%d\n", a < 0 && b > 0); /* 0:左侧为假 */
printf("%d\n", a < 0 && b < 0); /* 0:两者都为假 */
短路求值:如果左侧为假,右侧不会执行:
int x = 0;
if (x != 0 && 10 / x > 5) { /* x != 0 为假,右侧 10/x 不会执行 */
printf("Safe division\n"); /* 不会输出,但也不会崩溃 */
}
/* 如果没有短路求值,10 / 0 会导致运行时错误 */
这个特性常用于安全检查和条件访问:
int *ptr = NULL;
/* 先检查指针是否有效,再解引用 */
if (ptr != NULL && *ptr > 0) {
printf("Value: %d\n", *ptr);
}
/* 如果 ptr 为 NULL,*ptr 不会执行,避免空指针解引用 */
逻辑或 ||
|| 要求两侧表达式至少一个为真,结果才为真:
int a = 5, b = -10;
printf("%d\n", a > 0 || b > 0); /* 1:左侧为真 */
printf("%d\n", a < 0 || b > 0); /* 0:两者都为假 */
printf("%d\n", a > 0 || b < 0); /* 1:两者都为真 */
短路求值:如果左侧为真,右侧不会执行:
int x = 10;
if (x == 10 || expensive_function()) { /* x == 10 为真,expensive_function() 不会调用 */
printf("Short-circuited\n");
}
常用于提供默认值或备选方案:
int port = get_configured_port();
if (port <= 0 || port > 65535) /* 配置无效 */
port = 8080; /* 使用默认值 */
逻辑非 !
! 将真变为假,假变为真:
int flag = 1;
printf("%d\n", !flag); /* 0 */
printf("%d\n", !!flag); /* 1:双重否定,将非零值规范化为 1 */
int zero = 0;
printf("%d\n", !zero); /* 1 */
!! 常用于将任意非零值规范化为 1:
int value = 42;
bool normalized = !!value; /* 1 */
int empty = 0;
bool normalized2 = !!empty; /* 0 */
短路求值的实际应用
安全访问数组:
int arr[10];
int index = get_index();
if (index >= 0 && index < 10 && arr[index] > 0) {
/* index 越界时,arr[index] 不会执行 */
printf("Valid positive value\n");
}
分配内存后检查:
int *data = malloc(sizeof(int) * n);
if (data == NULL || initialize_data(data, n) != 0) {
/* data 为 NULL 时,initialize_data 不会执行 */
free(data);
return -1;
}
设置默认值:
char *name = get_user_name();
if (name == NULL || name[0] == '\0')
name = "Anonymous"; /* 空指针或空字符串时用默认值 */
与位运算符的区别
逻辑运算符 &&、||、! 与位运算符 &、|、~ 完全不同:
| 特性 | 逻辑运算符 | 位运算符 |
|---|---|---|
| 操作对象 | 表达式真假 | 整数的每一位 |
| 结果 | 0 或 1 | 按位计算的结果 |
| 短路求值 | 有 | 无 |
| 适用类型 | 任何标量 | 整数 |
int a = 1, b = 2;
printf("%d\n", a && b); /* 1:两者都非零 */
printf("%d\n", a & b); /* 0:01 & 10 = 00(二进制) */
printf("%d\n", a || b); /* 1:至少一个非零 */
printf("%d\n", a | b); /* 3:01 | 10 = 11(二进制) */
对布尔值应始终使用逻辑运算符 &&、||、!,它们有短路求值且语义清晰。位运算符 &、|、^ 用于整数位操作。
常见错误
用 & 代替 &&:
if (ptr != NULL & *ptr > 0) { /* 错误:使用位与 */
/* ptr 为 NULL 时,*ptr 仍会执行,导致崩溃! */
}
if (ptr != NULL && *ptr > 0) { /* 正确:逻辑与,短路求值保护 */
/* ... */
}
混淆优先级:
if (a > 0 && b > 0 || c > 0) /* 等价于 (a > 0 && b > 0) || c > 0 */
/* ... */
if (a > 0 && (b > 0 || c > 0)) /* 明确优先级 */
/* ... */
&& 优先级高于 ||,但显式括号更清晰。
过度依赖短路求值:
/* 晦涩:利用短路求值赋值 */
(x > 0) && (y = x); /* 如果 x > 0,y = x */
/* 清晰 */
if (x > 0)
y = x;
虽然 && 和 || 的短路求值可以用于条件执行,但用 if 语句更清晰。