C 运算符
运算符是 C 用来操作数据的符号。C 的运算符极其丰富——不仅有算术、关系、逻辑,还有强大的位运算(直接操作二进制位),这也是 C 接近硬件的体现之一。本篇系统过一遍 C 的所有运算符。
一、算术运算符
最基础的运算符,用于数学计算:
#include <stdio.h>
int main(void) {
int a = 17, b = 5;
// 基本算术
printf("%d + %d = %d\n", a, b, a + b); // 22
printf("%d - %d = %d\n", a, b, a - b); // 12
printf("%d * %d = %d\n", a, b, a * b); // 85
printf("%d / %d = %d\n", a, b, a / b); // 3(整数除法,丢小数!)
printf("%d %% %d = %d\n", a, b, a % b); // 2(取余,只对整数)
// 浮点除法
double x = 17.0, y = 5.0;
printf("%.2f\n", x / y); // 3.40
// 自增自减
int i = 5;
printf("%d\n", i++); // 5(先用再加)
printf("%d\n", i); // 6
printf("%d\n", ++i); // 7(先加再用)
// 注意:整数除法截断小数
printf("%d\n", 1 / 2); // 0,不是 0.5!
return 0;
}几个易错点:
- 整数除法会截断小数:
1 / 2是 0 而不是 0.5。要算浮点,至少一个操作数是浮点(1.0 / 2)。 - 取余
%只能用于整数,浮点取余要用fmod函数。 - 自增
++/ 自减--:前置(++i)先加再用,后置(i++)先用再加。 - printf 里输出字面百分号要写
%%。
二、关系运算符
关系运算符比较两个值,结果是 1(真)或 0(假)。C 没有真正的布尔类型(C99 才有 _Bool / bool),用整数 0/1 表示真假。
#include <stdio.h>
#include <stdbool.h> // C99 的 bool 类型
int main(void) {
int a = 10, b = 20;
// 关系运算结果:1(真)或 0(假)
printf("%d\n", a > b); // 0(假)
printf("%d\n", a < b); // 1(真)
printf("%d\n", a >= 10); // 1
printf("%d\n", a <= 10); // 1
printf("%d\n", a == b); // 0(注意:== 不是 =)
printf("%d\n", a != b); // 1
// C89 没有 bool,用 int 0/1
// C99 引入 bool(来自 stdbool.h)
bool is_bigger = a > b;
printf("is_bigger = %d\n", is_bigger); // 0
// 经典坑:把 == 写成 =(赋值)
if (a = 5) { // 这是赋值!a 变成 5,非0,恒真
printf("总会执行\n");
}
return 0;
}新手最大的坑:把相等比较 == 写成赋值 =。if (a = 5) 是合法的(把 5 赋给 a,然后判断 a 非0,恒真),但几乎肯定是 bug。养成习惯:比较常量写左边 if (5 == a),这样写错成 = 编译器会报错。
三、逻辑运算符
逻辑运算符组合多个条件:
&&逻辑与(AND):两边都真才真||逻辑或(OR):一边真就真!逻辑非(NOT):取反
#include <stdio.h>
#include <stdbool.h>
int main(void) {
int age = 25;
bool is_student = true;
// 逻辑运算:&& 与、|| 或、! 非
// 结果同样:1(真)或 0(假)
// && (与):两边都真才真
printf("%d\n", (age >= 18) && (age <= 60)); // 1
// || (或):一边真就真
printf("%d\n", (age < 18) || (age > 60)); // 0
// ! (非):取反
printf("%d\n", !is_student); // 0
// 短路求值(重要!)
// && 如果左边为假,右边不计算
// || 如果左边为真,右边不计算
int x = 0;
if (x != 0 && 10/x > 1) { // x==0 时不会算 10/x,避免除0
printf("不会执行\n");
}
// C 把 0 当假,任何非 0 都当真(包括负数)
if (-1) printf("-1 也算真\n"); // 会打印
if (0) printf("0 才是假\n"); // 不打印
return 0;
}短路求值是逻辑运算的关键特性:
&&:如果左边为假,右边不会计算(整个表达式已经确定是假)。||:如果左边为真,右边不会计算(整个表达式已经确定是真)。
这个特性常用来避免空指针、除零等错误:if (p != NULL && p->value > 0)。如果 p 是 NULL,后面的访问就不会执行,避免了崩溃。
四、位运算符(C 的特色)
位运算直接对整数的三进制位操作,这是 C 区别于 Java/Python(虽然它们也有位运算,但用得少)的地方。底层编程、协议解析、权限控制都离不开它:
#include <stdio.h>
int main(void) {
unsigned char a = 0b11001010; // 202(二进制字面量 C14 起支持)
unsigned char b = 0b10110101; // 181
// 旧写法:a = 0xCA; b = 0xB5;(十六进制)
printf("a & b = %d\n", a & b); // 按位与:168 (10001000)
printf("a | b = %d\n", a | b); // 按位或:215 (11010111)
printf("a ^ b = %d\n", a ^ b); // 按位异或:47 (00101111)
printf("~a = %d\n", (unsigned char)~a); // 取反:53
printf("a << 2 = %d\n", a << 2); // 左移2位:808 (相当于 *4)
printf("a >> 2 = %d\n", a >> 2); // 右移2位:50 (相当于 /4)
// 经典技巧:位运算做权限标志
unsigned int perm = 0;
perm |= (1 << 0); // 加读权限
perm |= (1 << 1); // 加写权限
printf("有读权限? %d\n", (perm & (1 << 0)) != 0); // 1
printf("有执行权限? %d\n", (perm & (1 << 2)) != 0); // 0
// XOR 加密/解密(对称)
char c = 'A';
char key = 7;
char encrypted = c ^ key; // 加密
char decrypted = encrypted ^ key; // 解密回 'A'
return 0;
}6 个位运算符:
&按位与:两位都 1 才 1|按位或:有一位 1 就 1^按位异或:两位不同才 1(相同则 0)~按位取反:0 变 1,1 变 0<<左移:二进制位整体左移,低位补 0(相当于乘 2 的 n 次方)>>右移:二进制位整体右移(正数高位补 0,相当于除 2 的 n 次方)
常见用途:(1) 权限标志位(一个 int 存多个开关)、(2) XOR 加密(对称,加密解密用同一操作)、(3) 位掩码(取某几位)、(4) 高性能乘除(左移代替乘 2)。
五、其他运算符
#include <stdio.h>
int main(void) {
int a = 10, b = 20;
// 1. 三目条件运算符 ?:
int max = (a > b) ? a : b; // 等价于 if-else 简写
printf("max = %d\n", max); // 20
// 2. 复合赋值运算符
int x = 5;
x += 3; // 等价 x = x + 3 = 8
x -= 2; // x = 6
x *= 4; // x = 24
x /= 5; // x = 4
x %= 3; // x = 1
x <<= 2; // x = 4
x &= 0xFF;// 位运算也支持复合
// 3. sizeof(也是运算符)
printf("int 大小: %zu\n", sizeof(int));
// 4. 逗号运算符(从左到右,结果是最后一个)
int r = (1, 2, 3);
printf("r = %d\n", r); // 3
// 主要用在 for 循环里写多个表达式
// 5. 取地址 &、解引用 *(指针篇细讲)
int n = 42;
int *p = &n;
printf("*p = %d\n", *p); // 42
// 6. 成员访问 . 和 -> (结构体篇细讲)
return 0;
}几个特殊的:
- 三目
?::条件运算符,条件 ? 真值 : 假值,是 if-else 的简写。 - 复合赋值:
+=-=*=/=%=&=|=<<=等,都是"运算后赋值"的简写。 - 逗号
,:从左到右依次计算,结果是最后一个。主要用在 for 循环。 - sizeof:求类型/变量的字节数,编译期运算。
- 取地址
&和解引用*:指针的核心,下一篇细讲。
六、运算符优先级(简化版)
C 有 15+ 级优先级,死记很难,记住几个常用规则就够:
- 括号
()优先级最高,有歧义就加括号(最稳的做法)。 - 单目运算符(
!~++--&*sizeof)> 算术 > 关系 > 逻辑 > 赋值。 - 算术里:
* / %>+ -。 - 关系里:
< > <= >=>== !=。 - 逻辑里:
!>&&>||。 - 赋值
=优先级最低(除逗号外)。
实际写代码时,拿不准就加括号——既不会出错,又能提高可读性。比如 a & b == c 实际是 a & (b == c)(== 优先级高于 &),如果想先做位与,必须写 (a & b) == c。
小结
算术 + - * / %、关系 > < == !=、逻辑 && || !、位运算 & | ^ ~ << >> 是 C 运算符的核心。注意整数除法截断、== 别写成 =、逻辑短路求值。优先级拿不准就加括号。下一篇我们看 C 的控制流——让程序学会判断和循环。
← 上一篇 C 数据类型
下一篇 C 控制流 →