语句基础

C++ 中大多数语句都以 分号 ; 结束。

最常见的一类是表达式语句:在表达式后加分号。

1
ival + 5;   // 表达式语句

空语句

空语句只有一个分号:

1
;

用途:

  • 语法上需要一条语句,但逻辑上什么也不做

例如:

1
2
while (*p++ != '\0')
; // 空循环体

建议给空语句加注释,避免误以为是漏写代码。

复合语句(块)

复合语句就是用 {} 括起来的一组语句和声明,也叫

1
2
3
4
{
int x = 0;
++x;
}

特点:

  • 块本身也是一条语句
  • 一个块就是一个作用域
  • 块末尾不加分号

条件语句

C++ 常用的条件语句有:

  • if
  • switch

if 语句

根据条件是否为真来决定是否执行某条语句。

基本形式

1
2
if (condition)
statement

如果 condition 为真,就执行 statement

if-else

1
2
3
4
if (condition)
statement1
else
statement2
  • 条件为真,执行 statement1
  • 否则执行 statement2

条件部分

condition 必须放在圆括号里,可以是:

  • 一个表达式
  • 一个带初始化的变量声明

并且其结果必须能转换为 bool

例如:

1
2
if (x > 0)
cout << "positive";

else 的匹配规则

在嵌套的 if-else 中:

else 总是与离它最近的、尚未匹配的 if 配对。

例如:

1
2
3
4
5
if (a)
if (b)
foo();
else
bar();

这里 else 匹配的是内层 if (b)

为避免歧义,建议适当使用花括号。

switch 语句

switch 适用于:
根据一个整数值在多个固定分支中选择其一执行。

基本形式

1
2
3
4
5
6
7
8
9
10
11
switch (expression) {
case value1:
statement1;
break;
case value2:
statement2;
break;
default:
statement3;
break;
}

执行过程

  1. 先计算 expression
  2. 将结果与各个 case 标签比较
  3. 若匹配成功,从该 case 开始执行
  4. 一直执行到:
  • 遇到 break
  • switch 结束

case 标签要求

  • case 标签的值必须是整型常量表达式
  • 不允许两个 case 值相同

例如:

1
2
case 1:
case 2:

是合法的,但:

1
case x:   // x 不是常量表达式时不合法

default

default 表示:

  • 当所有 case 都不匹配时执行的分支
1
2
3
default:
cout << "other";
break;

default 不是必须的,但通常建议写上。

穿透(fall through)

如果某个 case 后没有 break,程序会继续执行后面的分支。

1
2
3
4
5
6
switch (n) {
case 1:
cout << "one ";
case 2:
cout << "two ";
}

如果 n == 1,输出:

1
one two

这种现象叫穿透
如果不想穿透,记得写 break

迭代语句(循环)

循环语句用于重复执行一段代码,直到条件不满足。

C++ 常见循环:

  • while
  • for
  • 范围 for
  • do while

while 语句

只要条件为真,就一直执行循环体。

1
2
while (condition)
statement

执行顺序:

  1. 先判断条件
  2. 条件为真,执行循环体
  3. 再次判断条件

特点:

  • 可能一次都不执行
  • 适合循环次数不确定的情况

传统 for 语句

适合已知循环变量变化规律的场景。

基本形式

1
2
for (init-statement; condition; expression)
statement

含义:

  • init-statement:初始化
  • condition:循环条件
  • expression:每轮结束后的更新操作

执行顺序

执行过程相当于:

  1. 先执行一次 init-statement
  2. 判断 condition
  3. 若为真,执行循环体
  4. 执行 expression
  5. 再次判断 condition

说明

init-statement 可以是:

  • 声明语句
  • 表达式语句
  • 空语句

例如:

1
2
for (int i = 0; i < 10; ++i)
cout << i;

作用域

若在 for 头中定义变量,该变量只在 for 循环内部有效。

1
2
3
4
for (int i = 0; i < 3; ++i) {
// i 可见
}
// i 不可见

三部分都可省略

1
2
3
for (;;) {
// 无限循环
}

若省略 condition,效果等价于恒为 true

范围 for 语句

C++11 引入,用于遍历一个序列中的所有元素。

基本形式

1
2
for (declaration : expression)
statement

其中:

  • expression 必须是一个可遍历的序列
  • declaration 用来接收当前元素

例如:

1
2
for (char c : s)
cout << c;

适用对象

范围 for 可用于:

  • 数组
  • string
  • vector
  • 其他支持 beginend 的类型

修改元素时要用引用

1
2
3
4
5
for (auto c : s)       // 拷贝,不改原值
c = toupper(c);

for (auto &c : s) // 引用,可修改原字符串
c = toupper(c);

若要修改序列中的元素,必须使用引用。

do while 语句

while 类似,但先执行循环体,再判断条件

1
2
3
do
statement
while (condition);

特点:

  • 至少执行一次
  • 结尾必须有分号

例如:

1
2
3
4
5
int x = 0;
do {
cout << x << endl;
++x;
} while (x < 3);

注意

因为条件在后面,所以不能在条件部分定义变量

跳转语句

跳转语句会中断正常的执行流程。

常见跳转语句:

  • break
  • continue
  • goto
  • return

break

break 用于立即结束最近的一层:

  • while
  • do while
  • for
  • switch

例如:

1
2
3
4
while (cin >> x) {
if (x < 0)
break;
}

continue

continue 用于结束当前这一轮循环,立即进入下一轮。

只能用于循环中:

  • for
  • while
  • do while

例如:

1
2
3
4
5
for (int i = 0; i < 10; ++i) {
if (i % 2 == 0)
continue;
cout << i << " ";
}

输出奇数。

breakcontinue 的区别

  • break:直接结束整个循环
  • continue:只跳过当前这一轮

goto

goto 用于无条件跳转到同一函数内的某个标签处。

语法:

1
goto label;

标签形式:

1
2
label:
statement

例如:

1
2
3
4
goto end;
cout << "hello";
end:
return;

一般不推荐使用 goto,因为会让程序流程混乱、难维护。

异常处理基础

异常处理用于把“发现错误”和“处理错误”分开。

C++ 的异常机制主要包括:

  • throw:抛出异常
  • try:监视可能出错的代码
  • catch:捕获并处理异常

throw 表达式

当程序遇到自己无法处理的问题时,可以使用 throw 抛出异常。

1
throw runtime_error("error");

含义:

  • 中断当前正常执行流程
  • 把异常交给外层的异常处理代码

try-catch 语句块

基本形式:

1
2
3
4
5
6
try {
// 可能抛出异常的代码
}
catch (ExceptionType e) {
// 处理异常
}

执行过程:

  1. 先执行 try 块中的代码
  2. 若没有异常,跳过所有 catch
  3. 若发生异常,寻找匹配的 catch
  4. 找到后执行对应处理代码

多个 catch

一个 try 后面可以跟多个 catch

1
2
3
4
5
6
7
8
9
try {
// ...
}
catch (runtime_error &err) {
// ...
}
catch (exception &err) {
// ...
}

会选择第一个匹配的 catch

标准异常

标准库提供了一组常用异常类型,定义在不同头文件中。

常见异常类:

异常类型 含义
std::exception 大多数标准异常的基类
std::runtime_error 运行时错误
std::logic_error 逻辑错误
std::out_of_range 越界错误
std::invalid_argument 非法参数

使用异常类通常需要包含:

1
#include <stdexcept>

获取异常信息

标准异常通常提供成员函数:

1
what()

用于返回错误信息。

例如:

1
2
3
catch (std::runtime_error &err) {
std::cout << err.what() << std::endl;
}

易错点总结

块结尾不加分号

1
2
3
{
int x = 0;
} // 这里不加分号

ifelse 最好配合花括号

避免 else 绑定到错误的 if

switch 中别忘了 break

否则会发生穿透。

while 可能一次都不执行,do while 至少执行一次

范围 for 默认是拷贝

1
for (auto x : v)   // 修改 x 不影响 v

若要修改原元素:

1
for (auto &x : v)

continue 只结束本轮,break 结束整个循环

goto 尽量不用

除非非常特殊的底层控制场景。

异常抛出后,后续普通语句不会继续执行

1
2
throw runtime_error("error");
cout << "never"; // 不会执行