搜索

C++ 教程

C++基础

第1章 搭建开发环境与第一个程序 第2章 基本数据类型与变量 第3章 运算符与表达式 第4章 流程控制语句 第5章 数组与字符串 第6章 函数基础 第7章 指针基础 第8章 引用与const限定 第9章 动态内存管理 第10章 结构体与联合体 第11章 函数进阶

面向对象

第12章 面向对象编程入门 第13章 封装与访问控制 第14章 运算符重载 第15章 继承与派生 第16章 多态与虚函数

STL与进阶

第17章 模板编程基础 第18章 标准模板库(STL)容器 第19章 STL算法与迭代器 第20章 异常处理机制 第21章 文件输入输出 第22章 字符串类string 第23章 命名空间与作用域 第24章 类型转换操作符

现代C++

第25章 智能指针 第26章 移动语义与右值引用 第27章 Lambda表达式 第28章 多线程编程基础 第29章 模板元编程入门 第30章 设计模式基础 第31章 C++11/14新特性 第32章 C++17/20核心特性

实战进阶

第33章 性能优化技巧 第34章 调试与测试 第35章 项目结构与构建 第36章 网络编程入门 第37章 数据库连接操作 第38章 图形界面开发基础 第39章 综合项目实战 第40章 进阶学习路线

第3章 运算符与表达式

第3章 运算符与表达式

入门 👤 已掌握变量与数据类型,想写出能“计算”的程序 🔧 实现游戏得分计算、物理引擎、数据处理等核心逻辑
本章你将学到
  • 使用算术、关系、逻辑运算符进行数据操作
  • 理解运算符优先级,避免写出歧义代码
  • 掌握赋值、自增自减等常用复合运算符
核心概念运算符表达式

想象一下,你正在玩一个游戏:最终得分 = 基础分 + 连击奖励 * 难度系数。这个计算过程在C++里,就是由运算符和表达式完成的。运算符是告诉计算机做什么操作的符号(比如加号+),而表达式是由运算符、变量、常量组合起来,能计算出一个值的式子。

3.1 算术运算符:程序的计算器

算术运算符就是你的四则运算工具。除了基础的加减乘除,还有取余运算。

3.1.1 基础算术运算

运算符名称示例结果(假设a=10, b=3)
+加法a + b13
-减法a - b7
*乘法a * b30
/除法a / b3
%取模(求余)a % b1

这里有两个隐藏的坑。第一,整数除法会丢弃小数部分,直接取整。10 / 3的结果是3,不是3.333。想要得到小数结果,至少有一个操作数是浮点类型,比如10.0 / 3。

第二,取模运算符%只能用于整数类型(int, char等),不能用于float或double。我在一个传感器数据处理项目中,需要判断采样序号是否为10的倍数,用sampleIndex % 10 == 0就非常方便。

int score = 100;
int bonus = 20;
double multiplier = 1.5;
int finalScore = score + bonus; // 正确,结果为120
double finalScoreWithMultiplier = (score + bonus) * multiplier; // 正确,结果为180.0
int wrongDivision = score / 30; // 结果为3,小数部分丢失
double correctDivision = score / 30.0; // 结果为3.333...

常见误区

  • 混淆整数与浮点数除法:5 / 2结果是2,不是2.5。想要2.5,必须写成5.0 / 2或5 / 2.0。
  • 对浮点数使用取模运算:10.5 % 3.2会导致编译错误。取模运算的对象必须是整数。
  • 忽略除零错误:int x = 10 / 0;会导致程序运行时崩溃(除零异常)。实际开发中必须检查除数是否为零。

3.2 赋值与复合赋值运算符

等号=不是“等于”,而是“赋值”。它把右边的值计算出来,塞进左边的变量里。这就像把水(值)倒进一个贴了标签(变量名)的杯子。

基础的赋值很简单:int health = 100;。但C++提供了复合赋值运算符,能让代码更简洁。比如health = health + 10;可以写成health += 10;。

变量原有值
+
运算符与右值
→
计算新值
→
存回原变量

所有算术运算符都有对应的复合形式:+=, -=, *=, /=, %=。实际开发中,大部分团队会优先使用复合赋值运算符,因为它意图更清晰,有时编译器还能生成更优的代码。

int coins = 50;
coins += 10; // coins = coins + 10; 现在coins是60
coins -= 25; // coins = coins - 25; 现在coins是35
coins *= 2;  // coins = coins * 2;  现在coins是70
coins /= 7;  // coins = coins / 7;  现在coins是10 (70/7)
coins %= 3;  // coins = coins % 3;  现在coins是1 (10%3)

关键点:赋值表达式本身也有值

赋值操作a = 5本身也是一个表达式,它的值就是赋给a的值,也就是5。这允许你进行连续赋值:int x = y = 10;。执行顺序是从右向左:先把10赋给y,表达式y = 10的值是10,再把这个值赋给x。但为了代码清晰,我建议新手分开写。

3.3 自增与自减运算符

这是C++里非常常用但也容易出错的运算符。++表示加1,--表示减1。它们有前缀(++i)和后缀(i++)两种形式。

用一个生活类比:假设你排队取餐,前缀++i就像“先给我加一个号,我再取餐”,后缀i++就像“我先按现在的号取餐,取完你再给我加一号”。

int count = 5;
int a = ++count; // 前缀:先count加1变成6,再把6赋给a。结果:a=6, count=6
int b = count++; // 后缀:先把count当前值6赋给b,然后count加1变成7。结果:b=6, count=7

编者提示:自增/自减运算符在循环(第4章)和数组遍历(第5章)中会大量使用。理解它们的区别至关重要,否则会导致“差一错误”(off-by-one error),这是新手最常见的bug之一。

工程实践建议

除非在特定表达式中有明确需求,否则我建议单独使用自增/自减运算符,并优先使用前缀形式(++i)。例如:for(int i = 0; i < 10; ++i)。对于内置类型,前缀和后缀性能几乎没有差别,但养成使用前缀的习惯,在后续学习类对象时能避免不必要的临时对象拷贝。

3.4 关系与逻辑运算符:程序做判断

程序需要做选择:“血量是否大于0?”“玩家是否同时按下了跳跃键和方向键?”。这就需要关系运算符和逻辑运算符,它们计算的结果是布尔值(true或false)。

关系运算符

  • == 等于
  • != 不等于
  • > 大于
  • < 小于
  • >= 大于等于
  • <= 小于等于

逻辑运算符

  • && 逻辑与 (AND)
  • || 逻辑或 (OR)
  • ! 逻辑非 (NOT)

关系运算符很简单:10 > 5 为真,5 == 3 为假。逻辑运算符用于组合多个条件。&&要求两边都为真,结果才为真;||只要一边为真,结果就为真;!则取反。

int health = 75;
bool hasKey = true;
bool isDoorLocked = false;

bool isAlive = (health > 0); // true
bool canOpenDoor = hasKey && !isDoorLocked; // true && true = true
bool needWarning = (health < 20) || (health > 80); // false || false = false

关键点:逻辑运算符的“短路求值”

这是C++的一个重要特性。对于&&,如果左边表达式为false,整个结果已经确定为false,右边表达式根本不会执行。对于||,如果左边为true,右边也不会执行。这可以用来安全地编写条件语句。

int *ptr = nullptr;
// 安全的写法:先检查ptr是否非空,再解引用
if (ptr != nullptr && *ptr > 100) { // 如果ptr为空,*ptr > 100 不会执行,避免了崩溃
    // ...
}

3.5 运算符优先级与结合性

当表达式里出现多个运算符时,谁先算?1 + 2 * 3结果是7还是9?这由优先级决定。优先级相同时,计算方向由结合性决定(从左到右或从右到左)。

遇到复杂表达式
→
按优先级高低计算
→
优先级相同则按结合性计算
→
使用括号()明确顺序

你不需要记住所有优先级。实际开发中,我遵循一个简单原则:不确定就用括号。括号的优先级最高,可以明确指定计算顺序。写出(1 + 2) * 3比让读者去查1 + 2 * 3的优先级要清晰得多。

但有几个最常见的优先级关系你必须知道:

  1. 算术 > 关系 > 逻辑 > 赋值。例如:a = b > c && d; 先算b > c,再算&& d,最后赋值给a。
  2. 逻辑非!的优先级很高,高于关系运算符。所以!a == b等价于(!a) == b,而不是!(a == b)。
  3. 赋值运算符是右结合。所以a = b = 5等价于a = (b = 5)。
int x = 5, y = 10, z = 2;
bool result = x + 3 > y && z != 0; // 等价于 ((x + 3) > y) && (z != 0)
// 计算步骤:1. x+3=8, 2. 8>10=false, 3. z!=0=true, 4. false && true = false

// 清晰的做法:加上括号
bool clearResult = ((x + 3) > y) && (z != 0);

动手试一试

打开你的IDE,声明几个变量并尝试组合本章学到的运算符。

  1. 计算一个半径为5.0的圆的面积(面积 = π * 半径²,π取3.14159)。
  2. 声明一个整数counter初始为0。分别用前缀和后缀自增运算符增加它的值,并打印每次操作前后的值,观察区别。
  3. 模拟一个游戏状态:int health = 80; bool hasPotion = true; bool isPoisoned = false;。写出判断“玩家可以安全使用药水”(即生命值低于50且拥有药水且未中毒)的表达式,并打印结果。

检验你的理解

  1. 表达式int a = 10 / 4;执行后,a的值是多少?如果想得到2.5,代码应该怎么写?
  2. 以下代码片段输出什么?
    int i = 5;
    int j = ++i + i++;
    cout << "i=" << i << ", j=" << j;
  3. 假设int x = 5, y = 10;,表达式!x > y是true还是false?为什么?(提示:考虑优先级)

本章小结

  • 运算符是执行操作的符号,表达式是计算值的式子。算术运算符处理数值计算,关系与逻辑运算符产生布尔值用于判断。
  • 整数除法会截断小数,取模运算%仅用于整数。除零会导致运行时错误。
  • 前缀++i与后缀i++有本质区别:前缀先增后用,后缀先用后增。在复杂表达式中混用容易出错。
  • 逻辑运算符&&和||具有短路求值特性,这可以用于编写安全的条件检查代码。
  • 当表达式存在多个运算符时,使用括号()明确计算顺序是最清晰、最安全的做法,无需死记优先级表。

意见反馈

微信二维码 微信扫码关注 抖音二维码 抖音扫码关注