C++运算符优先级 #

一、优先级概述 #

运算符优先级决定了表达式中运算符的计算顺序。优先级高的运算符先计算。

1.1 基本规则 #

cpp
// 优先级高的先计算
int result = 2 + 3 * 4;  // 2 + (3 * 4) = 14,不是 (2 + 3) * 4 = 20

// 使用括号改变优先级
int result2 = (2 + 3) * 4;  // 20

1.2 结合性 #

当优先级相同时,结合性决定计算方向。

cpp
// 左结合:从左到右
int a = 10 - 5 - 2;  // (10 - 5) - 2 = 3

// 右结合:从右到左
int b = c = d = 10;  // d = 10, c = d, b = c

二、完整优先级表 #

2.1 优先级从高到低 #

优先级 运算符 名称 结合性
1 :: 作用域解析 左到右
2 () [] . -> ++(后缀) --(后缀) typeid 后缀运算符 左到右
3 ++(前缀) --(前缀) +(一元) -(一元) ! ~ *(解引用) &(取地址) sizeof new delete 一元运算符 右到左
4 .* ->* 成员指针 左到右
5 * / % 乘除 左到右
6 + - 加减 左到右
7 << >> 移位 左到右
8 < <= > >= 关系 左到右
9 == != 相等 左到右
10 & 位与 左到右
11 ^ 异或 左到右
12 | 位或 左到右
13 && 逻辑与 左到右
14 || 逻辑或 左到右
15 ?: 条件 右到左
16 = += -= *= /= %= &= |= ^= <<= >>= 赋值 右到左
17 , 逗号 左到右

2.2 记忆口诀 #

text
单目 > 算术 > 移位 > 关系 > 位运算 > 逻辑 > 条件 > 赋值 > 逗号

三、常见运算符优先级 #

3.1 算术运算符 #

cpp
int a = 2 + 3 * 4;      // 14,乘法优先
int b = 10 - 4 / 2;     // 8,除法优先
int c = 2 * 3 % 4;      // 2,从左到右

3.2 关系与逻辑运算符 #

cpp
// 关系运算符高于逻辑运算符
bool r1 = 5 > 3 && 2 < 4;  // (5 > 3) && (2 < 4) = true

// 比较运算符高于相等运算符
bool r2 = 5 > 3 == 2 < 4;  // (5 > 3) == (2 < 4) = true == true = true

// 逻辑与高于逻辑或
bool r3 = true || false && false;  // true || (false && false) = true

3.3 位运算与逻辑运算 #

cpp
// 位运算高于逻辑运算
bool r1 = 5 & 3 && 1;  // (5 & 3) && 1 = 1 && 1 = true

// 位运算优先级:& > ^ > |
int r2 = 5 | 3 ^ 1;    // 5 | (3 ^ 1) = 5 | 2 = 7
int r3 = 5 ^ 3 & 1;    // 5 ^ (3 & 1) = 5 ^ 1 = 4

3.4 赋值运算符 #

cpp
// 赋值优先级很低
int x;
int y = x = 10;  // y = (x = 10)

// 赋值与条件
int z = x > 5 ? 1 : 0;  // z = ((x > 5) ? 1 : 0)

四、常见陷阱 #

4.1 位运算与比较 #

cpp
int x = 5;

// 错误:优先级问题
if (x & 1 == 1) { }  // x & (1 == 1) = x & 1,但意图是 (x & 1) == 1

// 正确
if ((x & 1) == 1) { }

4.2 移位与加减 #

cpp
int x = 1;

// 错误
int a = x << 2 + 1;  // x << (2 + 1) = 8,不是 (x << 2) + 1 = 5

// 正确
int b = (x << 2) + 1;  // 5

4.3 条件运算符 #

cpp
int x = 5, y = 10;

// 错误
int a = x > y ? x++ : y++;  // 正确,但可能不是预期

// 复杂条件
int b = x > 0 ? x > y ? x : y : 0;  // 难以理解

// 更清晰的写法
int b = (x > 0) ? ((x > y) ? x : y) : 0;

4.4 指针与自增 #

cpp
int arr[] = {10, 20, 30};
int* p = arr;

// 优先级:后缀++高于*
int a = *p++;    // a = arr[0] = 10, p指向arr[1]
int b = *++p;    // p指向arr[2], b = arr[2] = 30
int c = ++*p;    // arr[2] = 31, c = 31
int d = (*p)++;  // d = 31, arr[2] = 32

五、使用括号的建议 #

5.1 何时使用括号 #

cpp
// 1. 不确定优先级时
int result = (a & b) | (c & d);

// 2. 复杂表达式
int value = ((a + b) * c - d) / e;

// 3. 位运算与比较
if ((flags & MASK) == VALUE) { }

// 4. 移位运算
int shifted = (value << shift) + offset;

5.2 不需要括号的情况 #

cpp
// 算术运算:遵循数学习惯
int area = width * height;
int perimeter = 2 * (width + height);

// 简单比较
if (x > 0 && y > 0) { }

// 简单赋值
x = y + z;

六、求值顺序 #

6.1 未定义的求值顺序 #

cpp
int i = 0;
int arr[] = {0, 0, 0};

// 未定义行为:i的自增顺序未定义
arr[i++] = i;  // 未定义!

// 更复杂的例子
int f();
int g();
int result = f() + g();  // f()和g()的调用顺序未定义

6.2 C++17的求值顺序改进 #

cpp
// C++17:函数参数求值顺序未定义
func(a(), b());  // a()和b()的顺序未定义

// C++17:赋值右侧先于左侧求值
int arr[10];
int i = 0;
arr[i++] = i;  // C++17:arr[0] = 1(定义行为)

6.3 序列点 #

cpp
// 序列点确保求值顺序
int x = 0;
int y = (x++, x++);  // 未定义行为(C++17前)

// 使用分号分隔
x++;
x++;
int y = x;

七、运算符重载与优先级 #

7.1 优先级不可改变 #

cpp
class Complex {
public:
    Complex operator+(const Complex& other);
    Complex operator*(const Complex& other);
};

Complex a, b, c;
Complex result = a + b * c;  // 仍然是 a + (b * c)

7.2 结合性不可改变 #

cpp
Complex a, b, c;
Complex result = a - b - c;  // (a - b) - c

八、实际示例 #

8.1 复杂表达式分析 #

cpp
int a = 2, b = 3, c = 4, d = 5;

int result = a + b * c - d / a << 1;

// 分析步骤:
// 1. b * c = 12
// 2. d / a = 2
// 3. a + 12 = 14
// 4. 14 - 2 = 12
// 5. 12 << 1 = 24

// 使用括号更清晰
int result = (((a + (b * c)) - (d / a)) << 1);

8.2 条件表达式 #

cpp
int score = 85;
char grade;

grade = score >= 90 ? 'A' :
        score >= 80 ? 'B' :
        score >= 70 ? 'C' :
        score >= 60 ? 'D' : 'F';

// 等价于
if (score >= 90) grade = 'A';
else if (score >= 80) grade = 'B';
else if (score >= 70) grade = 'C';
else if (score >= 60) grade = 'D';
else grade = 'F';

8.3 指针操作 #

cpp
int arr[] = {10, 20, 30, 40, 50};
int* p = arr;

// 分析以下表达式
int x = *p++;      // x = 10, p指向arr[1]
int y = *++p;      // p指向arr[2], y = 30
int z = ++*p;      // arr[2] = 31, z = 31
int w = (*p)++;    // w = 31, arr[2] = 32

九、最佳实践 #

9.1 使用括号明确意图 #

cpp
// 不推荐
if (x & MASK == VALUE)

// 推荐
if ((x & MASK) == VALUE)

9.2 分解复杂表达式 #

cpp
// 不推荐
int result = a + b * c - d / e << f & g | h;

// 推荐
int temp1 = b * c;
int temp2 = d / e;
int temp3 = a + temp1 - temp2;
int result = ((temp3 << f) & g) | h;

9.3 避免依赖求值顺序 #

cpp
// 不推荐
int x = f() + g() * h();

// 推荐
int temp1 = g();
int temp2 = h();
int temp3 = f();
int x = temp3 + temp1 * temp2;

9.4 使用constexpr表达式 #

cpp
constexpr int calculate(int a, int b, int c) {
    return a + b * c;
}

constexpr int result = calculate(2, 3, 4);  // 编译期计算

十、总结 #

优先级速查 #

类型 运算符 优先级
最高 :: () [] . ->
一元 ++ -- ! ~ * &
算术 * / % > + -
移位 << >>
关系 < <= > >= > == !=
位运算 & > ^ > |
逻辑 && > ||
条件 ?:
赋值 = +=
最低 ,

记住几点 #

  1. 使用括号明确意图
  2. 不确定时查表或使用括号
  3. 避免过于复杂的表达式
  4. 注意求值顺序问题

下一步,让我们学习控制流!

最后更新:2026-03-26