【C语言·007】算术运算符的优先级与结合性规则详解
来源:     阅读:2
易浩激活码
发布于 2025-11-13 21:46
查看主页

【C语言·007】算术运算符的优先级与结合性规则详解

许多人第一次被 C 语言“整顿”的瞬间,往往来自一行看似无害的表达式:为什么 1 + 2 * 3 是 7 而不是 9?为什么 a/b*ca/(b*c) 完全不同?再列如把 i++ 混进来,程序甚至可能直接掉进“未定义行为”的深坑。本文从工程实践出发,把“算术运算符的优先级与结合性”讲清楚,并配上容易踩坑的例子与可操作的改写准则。

一、算术运算符家族与“两个核心维度”

在 C 里,围绕数值计算的常见运算符主要有:

判断表达式值的两个核心维度

  1. 优先级(precedence):谁先算。
  2. 结合性(associativity):同优先级从左往右还是从右往左。

牢记一句话:先看优先级,再按结合性

二、只谈算术的“最小必记”表

下面这张表只保留算术相关条目,按从高到低排列,后缀/前缀也标出结合性:

层级

运算类别

代表

结合性

最高

括号分组

(expr)

—(强制分组)

后缀

后缀自增/自减

i++、i--

左结合

前缀

前缀自增/自减/一元正负

++i、--i、+i、-i

右结合

乘除模

乘、除、取余

*、/、%

左结合

加减

加、减

+、-

左结合

经验法则

乘除模优先于加减;同层级按左结合。

一元运算(如 -i++i)整体早于乘除;后缀 i++ 优先级高于前缀 ++i

括号能“越级”,是最稳的“保险丝”。

三、从规则到结果:逐步拆解 6 个表达式

例 1:基础混合

int v = 1 + 2 * 3 - 4 / 2; // 1 + 6 - 2 -> 5

先乘除(左结合),再加减,得到 5

例 2:同级左结合的威力

int v = 24 / 5 % 3; // (24/5)=4,再 4%3=1 -> v=1

若“凭感觉”写成 24 / (5 % 3),结果会变成 12,完全不同。

例 3:一元负号与乘法

int a = -2 * 3 + 1; // (-2*3)=-6,再 +1 -> -5

一元 - 在乘除之前结合到常量上。

例 4:自增参与但不作死

int i = 3;
int x = (i++) + 2; // 使用后再加1:x=5,i=4(安全)

只有一处修改 i,且另一侧不依赖 i,可读性与行为都清晰。

例 5:反例:未定义行为(UB)

int i = 2;
int y = i++ * ++i; // UB:一个序列点内对 i 的多次修改/读取冲突

这不是“结果不确定”,而是程序行为未定义——编译器可以做任何事情。修正:

int i = 2;
int a = i++;      // a=2, i=3
int b = ++i;      // i=4, b=4
int y = a * b;    // y=8

例 6:同级左结合不等同“先算大括号”

double t1 = 1.0/2*2; // 0.5*2 -> 1.0
double t2 = 1/2*2;   // 0*2   -> 0    (整数除法先发生)

优先级不解决类型问题,见下一节。

四、类型提升与“常规算术转换”——看不见的裁判

运算之前,C 会做一系列隐式转换,决定以什么精度计算:

  1. 整数提升(integer promotions) charshort 等会先提升为 int(或 unsigned int),再参与运算。
  2. 常规算术转换(usual arithmetic conversions) 为了让两侧“说同一种语言”:
  3. 若任一操作数是 long double/double/float,另一侧向其提升;
  4. 否则在整数层面按“类型等级与有无符号”规则对齐到共同类型(例如 intlong long 结果为 long long)。

这些规则直接决定除法与取模的语义

关键结论

1/2 按整数算是 01.0/2 才是 0.5

摆放顺序不变的情况下,类型决定了过程(double)a/ba/(double)b 都触发浮点除法,而 a/b 后再转 double 只是把 0 变成 0.0

有符号整数溢出是未定义行为;无符号整数按模 2^N 回绕(定义良好但常常不是你要的)。

五、五个高频坑位与可执行改写

  1. ++/-- 混进复杂表达式
  2. a += a++i = i++ + ++i(UB)
  3. ✅ 拆解成独立语句;或仅在独立行使用 i++/--i
  4. 指望优先级“自动分组”
  5. a/b*c 以为是 a/(b*c)
  6. ✅ 用括号表达意图:a / (b*c)(a/b) * c
  7. 整数与浮点混算“被降级”
  8. avg = sum / n;(整数)
  9. avg = sum / (double)n;avg = (double)sum / n;
  10. 溢出的隐蔽性
  11. int area = 100000 * 100000;(32 位溢出)
  12. long long area = 100000LL * 100000; 或使用范围检查。
  13. 宏里重复求值 #define SQR(x) ((x)*(x))
    int v = SQR(i++); // i++ 被求值两次 -> UB
  14. ✅ 改为内联函数:static inline int sqr(int x){ return x*x; }

六、工程实践的三条“硬规范”

七、十个“即刻辨析”练习(附解析)

自测时先写出你的第一反应,再对照解析找出“规则”在哪起作用。

  1. 3 + 4 * 2 - 8 / 3 3 + 8 - 2 = 9。先乘除,后加减。
  2. 24 / 5 % 3 (24/5)=44%3=1。同级左结合。
  3. -3*2 + 5 -6 + 5 = -1。一元 - 先结合到 3
  4. 1/2*21.0/2*2 :前者 0*2=0,后者 0.5*2=1.0。类型主导。
  5. 7 % -3-7 % 3 7%-3 == 1-7%3 == -1。余数与被除数同号。
  6. int i=2; int x = i++ + 2; x=4?不,是 x=4?——。应为 x=2+2=4?再加 1,i=3。 解析:后缀自增返回旧值,表达式求值后再加 1。
  7. int i=2; int y = i++ * ++i; 未定义行为。同一序列点对同一对象多次修改/读取。
  8. int a=5,b=2; double r = a/b; 2.0 而非 2.5。先整数除法,后转为 double
  9. int x=100000, y=100000; int z = x*y; :可能溢出(32 位)。用 long long z = 1LL*x*y;
  10. int n=3; int m = n+++n; :词法解析为 (n++) + n,结果 3+4=7n 先参与加法后变 4)。可读性极差,避免。

八、把规则内化为“肌肉记忆”的写法

当你把“优先级—结合性—类型转换”这三板斧用顺手,C 的算术表达式就不再是谜语,而是可控、可读、可维护的工程资产。

免责声明:本文为用户发表,不代表网站立场,仅供参考,不构成引导等用途。 系统环境
相关推荐
2018年南海区小学生程序设计竞赛详细答案
通过Apache Spark建立共识集群
SQL窗口函数知多少?
掌握HTTPClient的一系列相关问题,让你恍然大悟(六)
自动语音识别 Whisper 的 Python 应用:从语音到文字的高效神器
首页
搜索
订单
购物车
我的