用 if 做判断
程序时刻都在根据数据做出反应:及格还是不及格,有货还是售罄,有效还是无效。if 语句就是 C++ 根据条件选择执行哪段代码的方式。
if 接收括号中的一个条件,只有当条件为真时才执行花括号里的代码块:
条件 score >= 60 为真,所以消息会被打印。右花括号之后的那一行总会执行,因为它在 if 之外。把 score 改成 40,“passed”那一行就会被完全跳过。
条件会被转换为 bool。>、>=、==、!= 这样的比较本身就产生 bool,但 C++ 还会把任何非零数字当作真,把 0 当作假,所以 if (count) 的意思是“如果 count 不为零”。这种隐式转换很方便,但也是下文几个经典 bug 的根源。
加上 else
else 提供了“否则”的路径,也就是只在条件为假时执行的代码:
两个代码块中恰好执行一个:不会都执行,也不会都不执行。else 自己没有条件,它只是接住 if 没有处理的所有情况。
用 else if 串联多个分支
当结果多于两种时,用 else if 把条件串联起来。C++ 从上到下依次检查,执行第一个条件为真的代码块,然后跳过其余部分:
顺序很重要。84 先和 >= 90 比较(假),再和 >= 80 比较(真),于是停在 B,根本不会检查更低的分数线。这也是为什么你不需要写 score >= 80 && score < 90:能走到第二个分支,就已经保证 score 低于 90 了。把范围最窄或优先级最高的条件放在最前面。
注意,C++ 中没有 elif 关键字。else if 实际上就是一个 else,它的主体是另一个 if,花括号只是让它读起来像一架梯子。
一个大坑:= 与 ==
C 和 C++ 中最常见的 if 语句 bug,就是本想写 ==(比较),却写成了 =(赋值):
x = 5 把 5 赋给 x,整个表达式的值是 5,转换后为 true,所以这个分支总会执行,而 x 也被悄悄覆盖了。这段代码能通过编译,大多数编译器只会给出一个警告。有两种防范方法:开启警告(-Wall),让编译器把它标出来;也可以考虑把常量写在前面,即 if (5 == x),因为 if (5 = x) 是编译器会立即报出的硬错误。
同样的陷阱也藏在 bool 中:if (ready = true) 能编译,执行的是赋值;而 if (ready == true)(或直接写 if (ready))才是比较。
组合与嵌套条件
用 &&(与)、||(或)和 !(非)组合条件;如果某个检查只有在前一个检查通过后才有意义,就把一个 if 嵌套在另一个里面:
&& 和 || 具有短路特性:在 a && b 中,如果 a 为假,C++ 根本不会计算 b。这对防止非法内存访问至关重要:if (ptr != nullptr && ptr->ready) 是安全的,因为当 ptr 为空时,对指针的解引用会被跳过。能用 && 写成扁平结构时,就不要层层嵌套;扁平的条件比一座花括号金字塔更好读。
小心那些看起来像、实际却不是区间判断的连续比较:if (0 < x < 10) 并不判断区间。它先把 0 < x 计算成一个 bool(0 或 1),再拿它和 10 比较,结果永远为真。应该写成 if (0 < x && x < 10)。
三元运算符
如果你只是想在两个值之间选一个,三元运算符 ?: 可以用一行紧凑地写出来。condition ? a : b 读作“如果 condition 成立则为 a,否则为 b”:
三元运算符会产生一个值,你可以直接赋值、传参或打印,这是普通 if 做不到的。只在简单的选择中使用它:如果某个分支需要多条语句或实际的逻辑,完整的 if-else 依然清晰易读,而嵌套的三元运算符很快就会变成一道谜题。(有一个细节:两个分支必须有共同的类型,所以不要混用,比如一个字符串和一个 int。)
带初始化语句的 if(C++17)
从 C++17 开始,你可以直接在 if 中声明变量,它的作用域只限于这个 if/else。这样可以避免临时使用的辅助变量泄漏到周围的代码中:
; 之前的部分只执行一次,其结果在条件和 else 中都可见。这非常适合“先计算一个值,再根据它分支”的模式;而且由于结果的计算和转换只在开头进行一次,你就不会在后面重复计算,或意外使用一个过时的值。只要某个临时变量只在分支中需要,就可以用这种写法。
一个陷阱:省略花括号
当一个分支只有一条语句时,C++ 允许省略花括号,但这会招来一个隐蔽的 bug:缩进会骗人。
if (loggedIn)
cout << "Welcome\n";
cout << "Loading dashboard\n"; // NOT part of the if!
只有第一行受 if 控制;第二行无论 loggedIn 是什么都会执行,尽管缩进暗示的是另一回事。与之相关的另一个经典错误是多余的分号:if (x > 0); 用一个空主体结束了 if,于是后面的代码块会无条件执行。始终使用花括号,即使只有一行,这类错误大多就会消失。
下一步:switch
当每个分支检查的条件都不同时,if-else if 链是理想的选择。但如果你是在拿一个值和许多固定的可能值做比较,比如菜单选项、枚举或状态码,一长串 else if 会变得重复,而且容易排错顺序。switch 语句正是为此而设计的,这就是下一页的内容。
常见问题
C++ 中如何写 if-else 语句?
在 if 后面的括号里写条件,在花括号里写要执行的代码,再加上一个可选的 else 块,在条件为假时执行:if (score >= 60) { cout << "Pass"; } else { cout << "Fail"; }。条件会被转换为 bool,所以任何非零数字都算作真,0 算作假。
C++ 的 if 语句中 = 和 == 有什么区别?
== 比较两个值是否相等,而单个 = 是赋值。写成 if (x = 5) 会把 5 赋给 x,再判断 5(永远为真),这是一个经典 bug。它能通过编译,最多只有一个警告,所以要开启警告,也可以考虑把常量写在前面:if (5 == x)。
C++ 中的三元运算符是什么?
三元运算符 condition ? valueIfTrue : valueIfFalse 是一种紧凑的、能产生值的 if-else。例如 string label = age >= 18 ? "adult" : "minor"; 会从两个值中选出一个。简单地选择值时用它;如果每个分支都有实际的逻辑,就改用完整的 if-else。