运算符(operator)就是用来对变量、常量、表达式做运算的特殊符号。计算机常见的动作,加、减、比较、判断、按位运算,都靠它们。C语言 按功能可以把运算符分成六类,接下来看这六类各管什么。

越靠上,越先算 一元运算 ! ~ ++ -- + - (类型) sizeof 乘除 * / % 加减 + - 移位 << >> 比较 < <= > >= 相等 == != 位与 & 位异或 ^ 位或 | 逻辑与 && 逻辑或 || 三目 ?: 赋值 = += -= *= /= %= ... 越靠下,越后算
优先级从上到下递减,越靠上越先算;拿不准就加括号。

一、算术运算符

负责数学计算:加 +、减 -、乘 *、除 /、取余 %,以及一目(unary)的正负号 +x -x 和自增自减 ++ --。看一段完整例子:

C 语言算术运算符示例运行
#include <stdio.h>

int main(void) {
    int a = 25, b = 5;
    printf("a + b = %d\n", a + b);
    printf("a - b = %d\n", a - b);
    printf("a * b = %d\n", a * b);
    printf("a / b = %d\n", a / b);
    printf("a %% b = %d\n", a % b);   // %% 表示打印一个百分号
    printf("+a = %d\n", +a);
    printf("-a = %d\n", -a);
    printf("a++ = %d\n", a++);        // 先用旧值 25,再用完后 a 变 26
    printf("a-- = %d\n", a--);        // 先用旧值 26,再用完后 a 变 25
    return 0;
}

下面这段代码运行结果会依次得到:30、20、125、5、0、25、-25、25、26。有两处细节一定要留意。

  • 整数除以整数,结果是整数。25 和 5 都是 int,25/5 = 5;但 7/2 会得到 3(小数就扔掉了)。想留小数,至少一边必须是浮点:7.0 / 2 才是 3.5。
  • a++++a 时机不一样a++ 先把旧值交给表达式再用后自增;++a 先自增再把新值交出来。单独一行写 a++; 时两者没区别。

二、关系运算符

比较两个值的大小关系,结果要么真要么假。C 语言里真 = 1,假 = 0:

#include <stdio.h>

int main(void) {
    int a = 25, b = 5;
    printf("a < b  : %d\n", a < b);     // 0
    printf("a > b  : %d\n", a > b);     // 1
    printf("a <= b : %d\n", a <= b);     // 0
    printf("a >= b : %d\n", a >= b);     // 1
    printf("a == b : %d\n", a == b);     // 0  判断相等是两个等号
    printf("a != b : %d\n", a != b);     // 1  判断不相等
    return 0;
}

这里的 === 是两个不同的运算符:= 是赋值,== 才是比较。下面条件专题还会专门叮嘱一次。

三、逻辑运算符

把多个条件组合起来,或对条件取反。一共三个:

符号含义说明
&&逻辑与两边都为真,结果才为真
||逻辑或任一边为真,结果就为真
!逻辑非真变假、假变真
if (a > 0 && b > 0) { /* 两个都为正才进来 */ }
if (a > 0 || b > 0) { /* 至少一个为正就进来 */ }
if (!age) { /* age 为 0 时成立,等价于 age == 0 */ }

注意"非 0 即真":C 语言里任何非 0 的整数都是真,只有 0 是假。

四、位运算符

直接对整数的二进制位下手,低级编程、性能敏感的场合常用:

符号含义说明
&按位与同位都为 1 才为 1
|按位或同位有一个为 1 即为 1
^按位异或同位不同才为 1
~按位取反把所有位翻转
<<左移每位左移,右侧补 0,等于乘以 2
>>右移每位右移,相当于除以 2

入门阶段只要知道它们存在,等学到状态位、掩码(Bitmask)等内容时再回来深入研究即可。

五、赋值运算符

最简单的 = 把右侧赋给左侧。为了省事,C 语言把"计算 + 赋值"合成了一批复合赋值:

int a = 10;
a += 5;    // 等价于 a = a + 5;a 现在是 15
a -= 3;    // 等价于 a = a - 3;a 现在是 12
a *= 2;    // 等价于 a = a * 2;a 现在是 24
a /= 4;    // 等价于 a = a / 4;a 现在是 6
a %= 4;    // 等价于 a = a % 4;a 现在是 2

位运算同样有复合写法:&=|=^=>>=<<=。性质都一样,先算,再写回去。

六、其他运算符

还有一些运算符,不好归入前面介绍的几大类。

  • sizeof(一目运算符),编译时就给出运算对象的字节数。sizeof(int) 在 64 位桌上一般是 4。sizeof(arr)/sizeof(arr[0]) 是求数组元素个数的经典写法,数组篇会用到
  • 三目(条件)运算符 ? :,C 语言里唯一的三目运算符,可以当 if/else 的简写:大者 = (a > b) ? a : b; 条件为真取第二段,为假取第三段。
  • 取地址 & 与解引用 *&x 拿到 x 的地址,*p 取指针 p 指向的值。scanf 里 &age 就是取地址,指针篇会展开讲
  • 强制类型转换 (类型),比如 (double)7 / 2,先把 7 转成 double 再做除法,得到 3.5。

优先级怎么记

记住运算优先级规律:先乘除、后加减;算术运算优先于比较运算,比较运算又早于逻辑运算,赋值运算最后执行。拿捏不准优先级,直接加上括号,多多使用括号不会有问题。a + b > c && d == e 的真实执行流程:先计算 a+b,接着分别完成两次比较,最后再执行 && 逻辑判断。