📄 一句话概括
表达式有值(面粉),加个分号成语句(面包)。程序 = 一条条语句排队执行;表达式是语句里"算数的那部分",还能塞进更大的表达式。本节还揭一个安静的陷阱:a + b; 合法但白算。
一、表达式 vs 语句:分号前后的世界
| 表达式 | 语句 | |
|---|---|---|
| 定义 | 算出一个值的式子 | 程序的一个执行步骤 |
| 标志 | 有值有类型 | 分号或花括号收尾 |
| 比方 | 面粉(半成品) | 面包(成品) |
| 能否塞进更大式子 | 能 | 不能 |
a + b // 表达式:值是 a+b 的和
a + b; // 语句(表达式语句):算了,扔了
int s = a + b; // 语句:算了,存进 s
转换关系:任何表达式后面加个分号,就升级成"表达式语句"——这就是为什么 a + b; 能编译通过。
二、反方向:表达式里嵌表达式
表达式能当"值"用,所以可以层层嵌套:
int s = (a = 5) + (b = 3); // a=5 的值是 5,b=3 的值是 3 → s = 8
cout << (x > 0 ? 1 : -1) + 10; // 三目的结果再 +10
读嵌套式子的诀窍:先把每个括号里的小表达式换成它的值,一层层剥。这就是"面粉做面包"的反面——面粉也能先揉进别的面团。
但别写这种炫技代码——(a=5)+(b=3) 正确性没问题,可读性灾难。实际代码里赋值归赋值、算术归算术,分开写。
三、安静的陷阱:a + b;
int a = 3, b = 4;
a + b; // 编译通过!值 7 算出来直接扔掉
cout << a << endl; // a 还是 3——什么都没发生
这一行合法但无意义:算出 7、无处安放、随风而去。它九成九意味着你忘写了别的:
- 忘写
cout <<(想输出) - 忘写
=(想存起来) - 忘写
a +=(想累加)
编译器不会报错(语法没错),只可能给个"语句无效果"的警告。"能编译" ≠ "写对了"——这句话本章已经出现过三次了,这里是最隐蔽的一次。
四、语句的大家庭(提前点名)
程序里你会遇到的语句种类:
| 语句 | 例子 | 详讲 |
|---|---|---|
| 表达式语句 | a = 5; cout << x; |
本节 |
| 声明语句 | int a = 3; |
2.3.2 |
| 空语句 | ;(就一个分号) |
第 4 章的坑 |
| 块语句 | { ... } 把多条打包 |
2.1.5 花括号 |
| 控制语句 | if / for / while / return |
第 4、5 章 |
现在只需认脸:程序 = 语句排队;花括号把语句打包;控制语句决定哪些执行哪些跳过(第 4 章正式登场)。
五、30 秒实验
#include <iostream>
using namespace std;
int main() {
int a = 3, b = 4;
a + b; // 白算(看编译器有无警告)
cout << a + b << endl; // 7:表达式当值用
int s = (a = 5) + (b = 3); // 8:赋值表达式嵌套
cout << s << " " << a << " " << b << endl; // 8 5 3
; // 空语句:合法,什么都不干
return 0;
}
看到什么算成功:7 和 8 5 3。注意 a + b; 那行之后 a 仍是 3——白算实锤。Dev-C++ 的编译信息区(1.2.1 讲的五区之一)可能给警告,翻一翻。
30 秒实验:把 a + b; 改成 a += b;——a 变成 7。一字之差,白算变有用。
六、常见误区
- "表达式和语句是一回事"——表达式有值,语句是步骤。
a + b和a + b;隔着一个分号、差着一个世界。 - "
a + b;编译过了就没事"——白算。合法但无效的代码是最安静的 bug 温床。 - "嵌套赋值
(a=5)+(b=3)显得专业"——正确但难读。专业的是让人一眼看懂。 - "分号随便加"——
for(...);的分号会把循环体吞成空语句(第 5 章的坑),分号是有语义的。
自测四问(点开对答案)
1. `a + b` 和 `a + b;` 的区别?——前者是表达式(有值),后者是语句(值被扔掉)。 2. 为什么 `int s = (a = 5) + (b = 3);` 能算出 8?——赋值表达式有值:5 + 3。 3. `a + b;` 这种行最可能说明什么?——忘写 `cout <<` / `=` / `+=`,多半是 bug。 4. 花括号 `{}` 的作用是什么?——把多条语句打包成一块(块语句)。- ☐ 3.3.2 表达式中的类型转换(下一节,含实例04/05)