【全网最详细】C语言运算符优先级和结合性速查表(附记忆口诀)
先看一个最简单的例子:int a = 2, b = 3, c = 4;,那么 a + b * c 的结果是多少?小学就学过先乘除后加减,所以先算 b * c = 12,再加 a,结果是 14。C 语言里的大部分运算符也都遵循类似的“谁先算、谁后算”的约定,这个约定就叫优先级。
但 C 语言的运算符远不止加减乘除:全部加起来有 40 个左右,被分成 15 个优先级级别,里面有下标、成员选择、取地址、强制类型转换、位运算、逻辑运算……当它们混在同一个表达式里时,先算谁后算谁,完全由优先级决定;如果优先级相同,再由结合性决定从左往右还是从右往左。这两条规则,加上一个“目”的概念(一个运算符需要几个操作数),就是读懂一切 C 语言表达式的钥匙。
这篇文章把C语言运算符的优先级和结合性一次讲透:
看完这一篇,再复杂的表达式你也能一眼判断出计算顺序。
不过先把话说在前头:优先级不是用来死记硬背的。写代码时如果对顺序没有十足把握,直接加括号最稳妥,圆括号 ( ) 的优先级最高。
绝大多数运算符的结合性都是从左往右,比如乘和除同级,a / b * c 先算除法再算乘法:
上表中可以总结出如下规律:
这些容易出错的情况,希望读者好好在编译器上调试调试,这样印象会深一些。一定要多调试,光靠看代码,水平是很难提上来的。调试代码才是最长水平的。
最后再强调一遍:这张表不是用来背的,是拿来查的。写代码时遇到没把握的表达式,查表确认,或者干脆多加一对括号,既保证正确又方便别人阅读。用得多了,那些高频的优先级自然就熟了。
声明:当前文章为本站“玩转C语言和数据结构”官方原创,由国家机构和地方版权局所签发的权威证书所保护。
但 C 语言的运算符远不止加减乘除:全部加起来有 40 个左右,被分成 15 个优先级级别,里面有下标、成员选择、取地址、强制类型转换、位运算、逻辑运算……当它们混在同一个表达式里时,先算谁后算谁,完全由优先级决定;如果优先级相同,再由结合性决定从左往右还是从右往左。这两条规则,加上一个“目”的概念(一个运算符需要几个操作数),就是读懂一切 C 语言表达式的钥匙。
这篇文章把C语言运算符的优先级和结合性一次讲透:
- 先带你搞懂优先级、结合性、“目”三个基础概念;
- 再给出一份完整的 15 级速查表,写代码时随时可以查;
- 最后送上几条好记的记忆口诀。
看完这一篇,再复杂的表达式你也能一眼判断出计算顺序。
不过先把话说在前头:优先级不是用来死记硬背的。写代码时如果对顺序没有十足把握,直接加括号最稳妥,圆括号 ( ) 的优先级最高。
优先级、结合性和目
1)优先级:先算谁,后算谁
所谓优先级,就是当多个运算符出现在同一个表达式中时,决定先执行哪一个。乘法比加法优先、加法又比赋值优先,级别越高越先执行。看个例子:int a = 16, b = 4, c = 2; int d = a + b * c; // 先算 b*c = 8,再算 a + 8 = 24 int e = (a + b) * c; // 加了括号,先算 a + b = 20,再算 20 * 2 = 40同样两个表达式,加不加括号结果完全不同,这就是优先级在起作用。而括号的作用就是强行改变默认的计算顺序。
2)结合性:同级运算符谁先算
如果两个运算符优先级相同,又该先算谁呢?这时就要看结合性了。绝大多数运算符的结合性都是从左往右,比如乘和除同级,a / b * c 先算除法再算乘法:
int a = 16, b = 4, c = 2; int e = a / b * c; // 从左往右:先算 16/4 = 4,再算 4*2 = 8整个 C 语言里,结合方向为从右往左的只有三类:单目运算符、三目运算符、赋值运算符。最典型的是赋值:a = b = c = 100; 是先把 100 赋给 c,再把 c 的值赋给 b,最后赋给 a,一路从右往左“传递”下去。
3)目:一个运算符要几个操作数
“目”可以理解为“眼睛”。一个运算符运算时需要几个数,就叫几目运算符:- 单目运算符:只需要一个操作数,比如逻辑非 !a、负号 -a、自增 i++、取地址 &a 等;
- 双目运算符:需要两个操作数,C 语言里绝大多数运算符都是双目的,比如 a + b、a > b;
- 三目运算符:需要三个操作数。整个 C 语言只有唯一一个三目运算符,就是条件运算符 a ? b : c。
运算符优先级和结合性速查表
下表按优先级从高到低排列,级别数字越小,优先级越高(1 最高,15 最低)。建议把这张表收藏起来,写代码拿不准的时候回来查一下:| 优先级 | 运算符 | 名称或含义 | 使用形式 | 结合方向 | 说明 |
|---|---|---|---|---|---|
| 1 | [] | 数组下标 | 数组名[常量表达式] | 左到右 | |
| 1 | () | 圆括号 / 函数调用 | (表达式) / 函数名(形参表) | 左到右 | |
| 1 | . | 成员选择(对象) | 对象.成员名 | 左到右 | |
| 1 | -> | 成员选择(指针) | 对象指针->成员名 | 左到右 | |
| 2 | - | 负号运算符 | -表达式 | 右到左 | 单目运算符 |
| 2 | (类型) | 强制类型转换 | (数据类型)表达式 | 右到左 | |
| 2 | ++ | 自增运算符 | ++变量名 / 变量名++ | 右到左 | 单目运算符 |
| 2 | -- | 自减运算符 | --变量名 / 变量名-- | 右到左 | 单目运算符 |
| 2 | * | 取值运算符 | *指针变量 | 右到左 | 单目运算符 |
| 2 | & | 取地址运算符 | &变量名 | 右到左 | 单目运算符 |
| 2 | ! | 逻辑非运算符 | !表达式 | 右到左 | 单目运算符 |
| 2 | ~ | 按位取反运算符 | ~表达式 | 右到左 | 单目运算符 |
| 2 | sizeof | 长度运算符 | sizeof(表达式) | 右到左 | |
| 3 | * | 乘 | 表达式 * 表达式 | 左到右 | 双目运算符 |
| 3 | / | 除 | 表达式 / 表达式 | 左到右 | 双目运算符 |
| 3 | % | 余数(取模) | 整型表达式 % 整型表达式 | 左到右 | 双目运算符 |
| 4 | + | 加 | 表达式 + 表达式 | 左到右 | 双目运算符 |
| 4 | - | 减 | 表达式 - 表达式 | 左到右 | 双目运算符 |
| 5 | << | 左移 | 变量 << 表达式 | 左到右 | 双目运算符 |
| 5 | >> | 右移 | 变量 >> 表达式 | 左到右 | 双目运算符 |
| 6 | > | 大于 | 表达式 > 表达式 | 左到右 | 双目运算符 |
| 6 | >= | 大于等于 | 表达式 >= 表达式 | 左到右 | 双目运算符 |
| 6 | < | 小于 | 表达式 < 表达式 | 左到右 | 双目运算符 |
| 6 | <= | 小于等于 | 表达式 <= 表达式 | 左到右 | 双目运算符 |
| 7 | == | 等于 | 表达式 == 表达式 | 左到右 | 双目运算符 |
| 7 | != | 不等于 | 表达式 != 表达式 | 左到右 | 双目运算符 |
| 8 | & | 按位与 | 表达式 & 表达式 | 左到右 | 双目运算符 |
| 9 | ^ | 按位异或 | 表达式 ^ 表达式 | 左到右 | 双目运算符 |
| 10 | | | 按位或 | 表达式 | 表达式 | 左到右 | 双目运算符 |
| 11 | && | 逻辑与 | 表达式 && 表达式 | 左到右 | 双目运算符 |
| 12 | || | 逻辑或 | 表达式 || 表达式 | 左到右 | 双目运算符 |
| 13 | ?: | 条件运算符 | 表达式1 ? 表达式2 : 表达式3 | 右到左 | 三目运算符 |
| 14 | = | 赋值运算符 | 变量 = 表达式 | 右到左 | |
| 14 | += -= *= /= %= | 赋值(运算后赋值) | 变量 += 表达式 等 | 右到左 | |
| 14 | <<= >>= &= ^= |= | 赋值(位运算后赋值) | 变量 <<= 表达式 等 | 右到左 | |
| 15 | , | 逗号运算符 | 表达式, 表达式, … | 左到右 | 优先级最低 |
上表中可以总结出如下规律:
- 结合方向只有三个是从右往左,其余都是从左往右。
- 所有双目运算符中只有赋值运算符的结合方向是从右往左。
- 另外两个从右往左结合的运算符也很好记,因为它们很特殊:一个是单目运算符,一个是三目运算符。
- C语言中有且只有一个三目运算符。
- 逗号运算符的优先级最低,要记住。
- 此外要记住,对于优先级:算术运算符 > 移位运算符 > 关系运算符 > 逻辑运算符 > 赋值运算符。逻辑运算符中“逻辑非 !”除外。
运算符优先级和结核性记忆口诀
1)按大类记框架
算术运算符 > 移位运算符 > 关系运算符 > 位运算符 > 逻辑运算符 > 三目运算符 > 赋值运算符 > 逗号运算符(逻辑非 ! 是单目运算符,单独优先计算,不受这个框架约束)
2)记结合方向只看三类
从右往左结合的只有三类:单目运算符、三目运算符、赋值运算符;其余所有运算符全部从左往右结合。3)万能的保底写法
不确定优先级,就加()圆括号。圆括号优先级最高,想先算哪里就括哪里,既保证结果正确,又让代码一目了然,这本身就是一种良好的编程习惯。最容易出错的优先级问题(易错点逐个拆解)
优先级同为 1 级的几种运算符如果同时出现,很多初学者会迷糊。下表整理了最高频的 7 个易错情况,每一个都是真实踩过的坑:| 优先级问题 | 表达式 | 经常误认为的结果 | 实际结果 |
|---|---|---|---|
| . 的优先级高于 *(-> 操作符用于消除这个问题) | *p.f |
p 所指对象的字段 f,等价于: (*p).f |
对 p 取 f 偏移,作为指针,然后进行解除引用操作,等价于: *(p.f) |
| [] 高于 * | int *ap[] |
ap 是个指向 int 数组的指针,等价于: int (*ap)[] |
ap 是个元素为 int 指针的数组,等价于: int *(ap []) |
| 函数 () 高于 * | int *fp() |
fp 是个函数指针,所指函数返回 int,等价于: int (*fp)() |
fp 是个函数,返回 int*,等价于: int* ( fp() ) |
| == 和 != 高于位操作 | (val & mask != 0) | (val &mask) != 0 | val & (mask != 0) |
| == 和 != 高于赋值符 | c = getchar() != EOF | (c = getchar()) != EOF | c = (getchar() != EOF) |
| 算术运算符高于位移 运算符 | msb << 4 + lsb | (msb << 4) + lsb | msb << (4 + lsb) |
| 逗号运算符在所有运 算符中优先级最低 | i = 1, 2 | i = (1,2) | (i = 1), 2 |
这些容易出错的情况,希望读者好好在编译器上调试调试,这样印象会深一些。一定要多调试,光靠看代码,水平是很难提上来的。调试代码才是最长水平的。
总结
运算符优先级和结合性,本质上就是 C 语言为“表达式先算谁后算谁”定下的一套规则:优先级分 15 级,逗号最低、括号最高;同级看结合性,绝大多数从左往右,只有单目、三目、赋值三类从右往左。把“算术 > 关系 > 逻辑 > 赋值 > 逗号”这个大框架和“三个右结合”记在心里,再配合上面的速查表和口诀,日常写代码基本够用了。最后再强调一遍:这张表不是用来背的,是拿来查的。写代码时遇到没把握的表达式,查表确认,或者干脆多加一对括号,既保证正确又方便别人阅读。用得多了,那些高频的优先级自然就熟了。
声明:当前文章为本站“玩转C语言和数据结构”官方原创,由国家机构和地方版权局所签发的权威证书所保护。


ICP备案:
公安部网络备案: