关系与判等运算符
关系运算符(>、<、>=、<=)比较两个值的大小关系,判等运算符(==、!=)判断两个值是否相等或不等。它们的结果都是整数:关系成立时为 1(真),不成立时为 0(假)。这些运算符是条件判断和循环控制的基础,但浮点数比较和赋值混淆是常见的错误来源。
关系运算符
| 运算符 | 含义 | 示例 | 结果 |
|---|---|---|---|
> | 大于 | 5 > 3 | 1 |
< | 小于 | 5 < 3 | 0 |
>= | 大于等于 | 5 >= 5 | 1 |
<= | 小于等于 | 5 <= 4 | 0 |
int a = 10, b = 20;
printf("%d\n", a > b); /* 0 */
printf("%d\n", a < b); /* 1 */
printf("%d\n", a >= 10); /* 1 */
printf("%d\n", b <= 15); /* 0 */
关系运算符的结果是 int 类型:1 表示真,0 表示假。C 语言没有专门的布尔类型(C99 的 _Bool 本质也是整数),所以可以直接用整数参与逻辑运算:
int result = (5 > 3) + (2 > 4); /* 1 + 0 = 1 */
判等运算符
| 运算符 | 含义 | 示例 | 结果 |
|---|---|---|---|
== | 等于 | 5 == 5 | 1 |
!= | 不等于 | 5 != 3 | 1 |
int x = 10;
printf("%d\n", x == 10); /* 1 */
printf("%d\n", x == 20); /* 0 */
printf("%d\n", x != 10); /* 0 */
printf("%d\n", x != 20); /* 1 */
== 与 = 的混淆是 C 语言中最常见的错误:
if (x = 10) /* 将 10 赋给 x,表达式值为 10(真) */
printf("Always true\n");
if (x == 10) /* 比较 x 是否等于 10 */
printf("Maybe true\n");
编译器警告可以帮助捕获这类错误:
gcc -Wall -Wextra program.c
/* 警告:suggest parentheses around assignment used as truth value */
一种防御性编程习惯:将常量放在左边:
if (10 == x) /* 如果误写成 =,编译器会报错 */
/* ... */
/* 误写为赋值 */
if (10 = x) /* 编译错误:不能给常量赋值 */
/* ... */
浮点数比较的陷阱
浮点数由于精度误差,不能直接判等:
double a = 0.1 + 0.2;
double b = 0.3;
if (a == b)
printf("Equal\n"); /* 通常不会输出! */
else
printf("Not equal\n"); /* 输出这个 */
/* 查看实际值 */
printf("%.20f\n", a); /* 0.30000000000000004441 */
printf("%.20f\n", b); /* 0.29999999999999998890 */
正确比较浮点数:使用误差范围
#include <math.h>
#define EPSILON 1e-9
int double_equal(double a, double b)
{
return fabs(a - b) < EPSILON;
}
if (double_equal(0.1 + 0.2, 0.3))
printf("Approximately equal\n");
对于可能接近零的比较,使用相对误差:
int nearly_equal(double a, double b, double epsilon)
{
double diff = fabs(a - b);
double max_val = fmax(fabs(a), fabs(b));
if (max_val < epsilon)
return diff < epsilon; /* 两者都接近零 */
return diff / max_val < epsilon;
}
链式比较
C 语言不支持数学中的链式比较:
int x = 5;
/* 错误:不会判断 "x 是否在 1 到 10 之间" */
if (1 < x < 10) { /* 先算 1 < x = 1,再算 1 < 10 = 1,恒为真! */
printf("Always true\n");
}
/* 正确 */
if (1 < x && x < 10) { /* x 大于 1 且小于 10 */
printf("x is between 1 and 10\n");
}
字符比较
字符本质上是整数,可以直接比较:
char grade = 'B';
if (grade >= 'A' && grade <= 'F')
printf("Valid grade\n");
if (grade == 'A')
printf("Excellent!\n");
但注意:字符比较依赖字符编码(通常是 ASCII),在非 ASCII 环境(如 EBCDIC)中可能不成立。<ctype.h> 的函数更通用:
#include <ctype.h>
if (isalpha(grade)) /* 字母 */
if (isdigit(grade)) /* 数字 */
if (isupper(grade)) /* 大写 */
常见错误
混淆 == 和 =:
while (c = getchar()) { /* 将 getchar() 的值赋给 c,然后判断 c 是否为 0 */
/* 循环直到 EOF(通常 EOF = -1) */
}
/* 正确 */
while ((c = getchar()) != EOF) { /* 赋值后比较 */
/* ... */
}
比较不同类型的指针:
int *p = NULL;
if (p == 0) /* 合法:NULL 通常定义为 0 */
if (p == NULL) /* 更清晰 */
if (p == (void *)0) /* 最明确 */
有符号与无符号比较:
int s = -5;
unsigned int u = 10;
if (s < u) /* 危险:s 被转换为很大的无符号数 */
printf("-5 < 10\n"); /* 不会输出! */