C语言小课堂:运算优先级

同学们好!今天我们来聊聊C语言中一个看似不起眼,实则举足轻重的概念——运算优先级。它决定了表达式中各个运算符的执行顺序,理解它,才能让你的程序按照你预想的路径运行。

一、知识点讲解

  1. 运算优先级:它“是什么”?

在C语言中,一个表达式可能包含多个运算符,比如加减乘除、赋值、逻辑运算等等。当这些运算符同时出现时,计算机如何知道先计算哪个,后计算哪个呢?这就是“运算优先级”要解决的问题。

简单来说,运算优先级就是C语言为各种运算符预设的执行顺序规则。优先级高的运算符会先于优先级低的运算符执行。如果优先级相同,那么就按照“结合性”来决定执行顺序(通常是从左到右,但也有例外,比如赋值运算符是从右到左)。

  1. 何时用?为什么用?

何时用?只要你的C语言表达式中出现两个或两个以上的运算符,你就需要考虑运算优先级。无论是在编写简单的数学计算,还是复杂的条件判断、位操作,运算优先级的知识都必不可少。

确保计算结果的正确性: 没有优先级,2 + 3 * 4 可能会被错误地计算成 (2 + 3) * 4 = 20,而不是正确的 2 + (3 * 4) = 14。运算优先级保证了表达式按照数学上的常规逻辑进行求值。

提高代码可读性: 虽然可以通过括号来强制改变运算顺序,但了解优先级可以让你在不滥用括号的情况下,写出更清晰、更符合直觉的代码。

避免隐藏的bug: 许多新手程序员在编写复杂表达式时,由于不熟悉优先级而引入难以发现的逻辑错误。例如,if (a > 0 && b < 10)if (a > 0 || b < 10) 中, &&|| 的优先级就决定了它们的求值顺序。

与关联知识点的联系与区别:运算优先级与“结合性”是紧密相关的两个概念。当两个运算符优先级相同时,结合性就登场了。

特性 运算优先级 结合性
定义 决定不同运算符的执行顺序 决定相同优先级运算符的执行顺序
规则 优先级高的先执行 从左到右或从右到左
示例 * 优先于 +,所以 a + b * c 先算 b * c + 从左到右结合,所以 a + b + c 先算 a + b
  1. 怎么用?

C语言的运算符种类繁多,它们的优先级也各不相同。记住所有运算符的优先级可能有些困难,但掌握一些核心原则和常见运算符的优先级就足够了。

核心原则:

括号 () 优先级最高: 任何被括号括起来的表达式都会首先被计算。这是你强制改变运算顺序的利器。

单目运算符通常高于双目运算符: 例如,! (逻辑非)、 ++ (自增)、-- (自减) 的优先级通常高于 +-*/

算术运算符高于关系运算符,关系运算符高于逻辑运算符:

* / % (乘除模) > + - (加减)

+ - > < > <= >= (关系运算符)

< > <= >= > == != (相等性运算符)

== != > && (逻辑与)

&& > || (逻辑或)

赋值运算符优先级最低: 几乎所有其他运算符都比赋值运算符 = 的优先级高。

常见运算符优先级(从高到低,不完全列表):

优先级 运算符 结合性 描述
1 () [] . -> ++ -- (后缀) 左到右 圆括号、数组下标、结构体成员访问、后置自增/减
2 ++ -- (前缀) ! ~ + - * & sizeof 右到左 前置自增/减、逻辑非、按位取反、正负号、解引用、取地址、求字节数
3 * / % 左到右 乘、除、取模
4 + - 左到右 加、减
5 << >> 左到右 左移、右移
6 < <= > >= 左到右 关系运算符
7 == != 左到右 相等性运算符
8 & 左到右 按位与
9 ^ 左到右 按位异或
10 左到右
11 && 左到右 逻辑与
12
13 ? : 右到左 三目运算符
14 = += -= *= /= % = 右到左 赋值运算符及复合赋值运算符
15 , 左到右 逗号运算符

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <stdio.h>

int main() {
int a = 5, b = 2, c = 3, result;

// 示例1: 算术运算优先级
// 预期:先算 b * c (2 * 3 = 6),再算 a + 6 (5 + 6 = 11)
result = a + b * c;
printf("a + b * c = %d\n", result); // 输出 11

// 示例2: 使用括号改变优先级
// 预期:先算 (a + b) (5 + 2 = 7),再算 7 * c (7 * 3 = 21)
result = (a + b) * c;
printf("(a + b) * c = %d\n", result); // 输出 21

// 示例3: 逻辑运算与算术运算混合
// 预期:先算 a > b (5 > 2 为真),然后算 c == 3 (3 == 3 为真)
// 最后算 true && true (逻辑与为真)
if (a > b && c == 3) {
printf("条件为真: a > b && c == 3\n");
} else {
printf("条件为假\n");
}

// 示例4: 自增运算符与赋值运算符
// 预期:b++ 是后置自增,先使用 b 的当前值 (2) 进行计算,再将 b 增加到 3。
// 所以 result = a + 2 (5 + 2 = 7)。
result = a + b++;
printf("a + b++ = %d, b = %d\n", result, b); // 输出 7, b=3

// 示例5: 前置自增运算符与赋值运算符
// 预期:++c 是前置自增,先将 c 增加到 4,再使用 c 的新值 (4) 进行计算。
// 所以 result = a + 4 (5 + 4 = 9)。
result = a + (++c);
printf("a + (++c) = %d, c = %d\n", result, c); // 输出 9, c=4

return 0;
}

小贴士:当你对某个表达式的优先级不确定时,最稳妥、最清晰的做法就是使用括号 () 来明确指定运算顺序 。这不仅能避免错误,还能大大提高代码的可读性,让未来的你(或你的同事)一眼就能明白你的意图。

二、典型习题

选择题:下面C语言表达式 int x = 10, y = 5, z = 2; int result = x + y * z - 1;result 的值是多少?

A. 19

B. 29

C. 14

D. 12

判断题:表达式 a = b > c ? b : c; 中, > 运算符的优先级高于 ? : 三目运算符。

A. 正确

B. 错误

填空题:请问表达式 ! (5 > 3 && 2 < 1) 的最终结果是 ______ (填写0或1)。

编程题:编写一个C语言程序,计算表达式 (10 + 20) * 3 / 2 - 5 % 2 的结果,并打印出来。要求在表达式中不使用任何多余的括号(即只使用必要的括号来确保优先级)。

编程题(进阶):编写一个C语言程序,声明三个整型变量 a , b , c,并分别初始化为 7 , 3 , 5。 然后,计算以下表达式的值,并打印结果。请分析表达式中运算符的优先级和结合性。int final_result = a++ * b - c / 2 + ! (a > b && c < 10);

三、习题讲解

  1. 选择题:

下面C语言表达式 int x = 10, y = 5, z = 2; int result = x + y * z - 1;result 的值是多少?

A. 19

B. 29

C. 14

D. 12

答案:A

解析:根据C语言的运算优先级规则:

乘法 * 的优先级高于加法 + 和减法 - 。 所以,首先计算 y * z,即 5 * 2 = 10

表达式变为 result = x + 10 - 1;

加法 + 和减法 - 优先级相同,它们是左结合的,所以从左到右依次计算。

先计算 x + 10 ,即 10 + 10 = 20

再计算 20 - 1 ,即 19。 因此,result 的值为 19

  1. 判断题:

表达式 a = b > c ? b : c; 中, > 运算符的优先级高于 ? : 三目运算符。

A. 正确

B. 错误

答案:A

解析:根据C语言的运算符优先级表,关系运算符 > 的优先级(第6级)高于三目运算符 ? :(第13级)。 这意味着在 b > c ? b : c 这个表达式中,会首先计算 b > c 这个关系表达式,得到一个布尔结果(真或假),然后根据这个结果再执行三目运算符的条件选择。 所以,该说法是正确的。

  1. 填空题:

请问表达式 ! (5 > 3 && 2 < 1) 的最终结果是 ______(填写0或1)。

答案:1

解析:

括号内的运算优先:(5 > 3 && 2 < 1)

关系运算符优先于逻辑运算符:

5 > 3 :结果为真 (1)。

2 < 1 :结果为假 (0)。

逻辑与 && 运算:

真 && 假 (即 1 && 0):结果为假 (0)。

逻辑非 ! 运算:

! 假 (即 ! 0):结果为真 (1)。 因此,最终结果是 1

  1. 编程题:

编写一个C语言程序,计算表达式 (10 + 20) * 3 / 2 - 5 % 2 的结果,并打印出来。要求在表达式中不使用任何多余的括号(即只使用必要的括号来确保优先级)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

int main() {
// 表达式:(10 + 20) * 3 / 2 - 5 % 2
// 分析优先级:
// 1. 括号内的 (10 + 20) 最优先
// 2. 乘法 * 和除法 / 和取模 % 优先级相同,从左到右结合
// (10 + 20) * 3
// 结果再 / 2
// 5 % 2
// 3. 减法 - 最后计算
int result = (10 + 20) * 3 / 2 - 5 % 2;
printf("表达式 (10 + 20) * 3 / 2 - 5 %% 2 的结果是: %d\n", result);
// 注意:在printf的格式字符串中,如果要打印百分号%,需要使用 %% 进行转义。
return 0;
}

运行结果:

表达式 (10 + 20) * 3 / 2 - 5 % 2 的结果是: 44

解析:

(10 + 20) 首先计算,得到 30

30 * 3 计算,得到 90

90 / 2 计算,得到 45

5 % 2 计算,得到 1 (5除以2的余数)。

最后 45 - 1 计算,得到 44

  1. 编程题(进阶):

编写一个C语言程序,声明三个整型变量 a , b , c,并分别初始化为 7 , 3 , 5。 然后,计算以下表达式的值,并打印结果。请分析表达式中运算符的优先级和结合性。int final_result = a++ * b - c / 2 + ! (a > b && c < 10);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>

int main() {
int a = 7, b = 3, c = 5;
int final_result;

// 表达式:a++ * b - c / 2 + ! (a > b && c < 10);
// 详细分析过程:
// 1. 后缀自增 a++:a 的当前值 (7) 用于表达式计算,然后 a 变为 8。
// 所以 a++ * b 相当于 7 * 3 = 21。
// 2. 除法 c / 2:5 / 2 = 2 (整数除法)。
// 3. 括号内的逻辑运算 (a > b && c < 10):
// 此时 a 已经更新为 8。
// a > b 即 8 > 3,结果为真 (1)。
// c < 10 即 5 < 10,结果为真 (1)。
// 真 && 真 (1 && 1) 结果为真 (1)。
// 4. 逻辑非 ! (真):! 1 结果为假 (0)。
// 5. 乘法 * 和除法 / 优先级高于加法 + 和减法 -。
// 表达式现在简化为:21 - 2 + 0;
// 6. 减法 - 和加法 + 优先级相同,从左到右结合。
// 21 - 2 = 19
// 19 + 0 = 19
final_result = a++ * b - c / 2 + ! (a > b && c < 10);

printf("a = %d, b = %d, c = %d\n", 7, 3, 5); // 打印初始值
printf("a++ * b 的计算结果是 7 * 3 = 21, 此时 a 变为 8\n");
printf("c / 2 的计算结果是 5 / 2 = 2\n");
printf("! (a > b && c < 10) 的计算过程:\n");
printf(" (a > b) 即 (8 > 3) 为真 (1)\n");
printf(" (c < 10) 即 (5 < 10) 为真 (1)\n");
printf(" (真 && 真) 为真 (1)\n");
printf(" ! 真 为假 (0)\n");
printf("最终表达式等价于 21 - 2 + 0\n");
printf("final_result = %d\n", final_result);

// 打印表达式计算后 a 的最终值,以验证自增效果
printf("表达式计算后 a 的最终值: %d\n", a);

return 0;
}

运行结果:

a = 7, b = 3, c = 5
a++ * b 的计算结果是 7 * 3 = 21, 此时 a 变为 8
c / 2 的计算结果是 5 / 2 = 2
! (a > b && c < 10) 的计算过程:
(a > b) 即 (8 > 3) 为真 (1)
(c < 10) 即 (5 < 10) 为真 (1)
(真 && 真) 为真 (1)
! 真 为假 (0)
最终表达式等价于 21 - 2 + 0
final_result = 19
表达式计算后 a 的最终值: 8

解析:这个表达式综合了多种运算符,是考察优先级和结合性的好例子。

a++ (后置自增): a 的当前值 7 用于表达式的 * b 部分,然后 a 立即变为 8 。所以 a++ * b 实际是 7 * 3 = 21

c / 2 (整数除法):5 / 2 = 2

! (a > b && c < 10)

括号 () 优先级最高。

在括号内, >< (关系运算符) 优先级高于 && (逻辑与)。

a > b :此时 a 已经更新为 8 ,所以 8 > 3 为真 (1)。

c < 105 < 10 为真 (1)。

&&1 && 1 为真 (1)。

最后, ! (逻辑非) 优先级高于括号内的运算结果。! (1) 为假 (0)。

整个表达式现在简化为 21 - 2 + 0

-+ 优先级相同,从左到右结合。

21 - 2 = 19

19 + 0 = 19 。 所以 final_result 的最终值是 19

程序中的每一行代码,都有其先后的秩序。懂得运算的优先级,就如同明白了何事当先,如此,方能让那看似杂乱的逻辑,也流淌出清晰而准确的脉络。