在C++中,程序的执行通常是从上到下、一步一步顺序进行的。但如果只有顺序执行,程序就像只能直走的火车,无法根据不同情况做出选择或循环。 为了解决这个问题,C++提供了多种控制语句,让程序可以像聪明的机器人一样,根据不同的条件做出不同的反应,甚至可以反复做某件事,直到满足某个条件为止。

C++里最常见的语句就是“表达式语句”。只要在一个表达式后面加上分号,它就变成了一条语句。例如:
|int age = 18; // 这是一个赋值语句 std::cout << age << std::endl; // 这是一个输出语句
有时候你会看到一些“看起来没什么用”的语句,比如:
|x + 5;
这行代码会计算x+5的值,但结果没有被保存或输出,相当于什么都没发生。通常我们写语句,是希望它能产生“副作用”,比如修改变量的值、输出内容等。
最简单的语句其实就是一个分号:
|;
这叫做“空语句”,什么都不做。你可能会觉得它没用,但有时候在语法上必须要有一条语句,而逻辑上又什么都不需要做,这时空语句就派上用场了。比如:
|while (std::cin >> name && name != "exit") ; // 这里用空语句表示什么都不做,只是不断读取,直到遇到exit
建议在空语句旁边加个注释,提醒别人你是故意什么都不做的。
有时候你会不小心多写一个分号,比如:
|int sum = a + b;;
这其实没什么大问题,第二个分号只是一个空语句。但如果你在循环或条件语句后面多写了分号,可能会导致逻辑错误。例如:
|while (i < 10); ++i;
这里,循环体其实是空语句,++i; 这行每次循环后都会单独执行,可能导致死循环。
缩进虽然看起来像是++i在循环里,但实际上由于第一行的分号原因,编译器会认为++i不是循环体的一部分。
因此我们写代码时一定要小心分号的位置!
有时候你希望在某些条件下执行多条语句,这时就需要用大括号把它们包起来,形成一个“代码块”:
|if (score >= 60) { std::cout << "及格了!" << std::endl; passedCount++; }
大括号里的内容被当作一条“复合语句”,也叫“代码块”。在C++里,代码块本身就是一个作用域,里面定义的变量只在这个块里有效。你也可以写一个空的代码块:
|while (std::cin >> cmd && cmd != "quit") { // 什么都不做 // 这个大括号中是一个单独的作用域 }
你可以在if、while、for等控制结构的括号里定义变量,这些变量只在这个结构内部有效。例如:
|while (int num = getNumber()) { std::cout << "你输入了:" << num << std::endl; } // 这里num已经失效,不能再用了
如果你希望变量在循环外也能用,就要把它定义在外面:
|int num = 0; while ((num = getNumber()) != 0) { // 使用num } // 这里num依然有效
if语句让程序可以根据条件决定是否执行某些代码。比如:
|if (temperature > 30) std::cout << "今天很热,记得多喝水!" << std::endl; else std::cout << "温度适宜,祝你有美好的一天!" << std::endl;
if后面跟着一个条件,条件为真时执行if后的语句,否则执行else后的语句。你也可以只写if,没有else。
有时候你需要根据多个条件做不同的事情,可以用“嵌套if”:
|if (score == 100) std::cout << "满分,太棒了!" << std::endl; else if (score >= 90) std::cout << "优秀!" << std::endl; else if (score >= 60) std::cout << "及格。" << std::endl; else std::cout
注意:如果if和else后面要执行多条语句,一定要用大括号包起来,否则只有第一条语句属于if或else。
例如:
|if (isRainy) std::cout << "带伞出门" << std::endl; std::cout << "小心路滑" << std::endl; // 这行其实不属于if
上面只有第一行属于if,第二行无论是否下雨都会执行。养成if/else后总是加大括号的习惯,可以避免很多低级错误。 正确写法如下:
|if (isRainy) { std::cout << "带伞出门" << std::endl; std::cout << "小心路滑" << std::endl; }
当if语句嵌套时,else总是和最近的、还没有else的if配对。例如:
|if (hasTicket) if (age < 18) std::cout << "未成年人票" << std::endl; else std::cout << "成人票" << std::endl;
这里的else其实是和if (age < 18)配对的,而不是最外层的if。要想让else和外层if配对,必须用大括号把内层if包起来。
switch语句适合处理“多选一”的情况,比如根据用户输入的菜单编号执行不同操作。例如:
|int option; std::cin >> option; switch (option) { case 1: std::cout << "你选择了查询余额" << std::endl; break; case 2: std::cout << "你选择了存款" << std::endl; break; case 3: std::cout << "你选择了取款"
switch会根据括号里的表达式结果,跳到对应的case标签执行后面的代码。如果没有匹配的case,就执行default后的代码。每个case后面通常要加break,否则会“贯穿”到下一个case继续执行。 请看下面的例子。
如果你想让多个case执行同样的操作,可以把它们写在一起,不加break。例如:
|char ch; std::cin >> ch; switch (ch) { case 'a': case 'e': case 'i': case 'o': case 'u': std::cout << "你输入了元音字母" << std::endl; break; default: std::cout << "你输入的不是元音字母" <<
这里只要输入的是元音字母,都会输出同样的提示。
default分支在没有任何case匹配时执行。即使你暂时不需要处理,也可以写一个空的default,表示你考虑过这种情况。
在switch语句的每个case里,如果你要定义并初始化变量,最好用大括号包起来,否则可能会因为跳转导致变量未初始化而出错。例如:
|switch (mode) { case 1: { std::string name = getName(); std::cout << name << std::endl; break; } case 2: // ... break; }
这样可以保证变量只在需要的case里有效,避免作用域混乱。
在编程中,我们经常会遇到需要重复做某件事的场景,比如让小明每天早上背单词,直到他记住为止, 或者让机器人一遍遍扫地,直到房间干净。C++为我们提供了多种循环语句,让程序可以像勤劳的小蜜蜂一样,不知疲倦地完成重复任务。
while循环就像“只要……就一直做……”的口头禅。它会先判断条件,如果条件成立,就执行循环体,然后再判断一次条件,如此反复,直到条件不成立为止。
举个例子,假如你想让小朋友一直输入数字,直到输入0为止:
|int number; std::cout << "请输入数字(输入0结束):" << std::endl; std::cin >> number; while (number != 0) { std::cout << "你输入了:" << number << std::endl; std::cin >> number; } std::cout << "输入结束,再见!" << std::endl;
在这个例子里,每次循环都会让用户输入一个数字,只要不是0,程序就会继续提示输入。只有当输入0时,循环才会结束。
for循环更像是“我已经知道要做几次”的场景。比如老师让你做10道数学题,每做完一道题就打个勾,直到做完为止。
|for (int i = 1; i <= 10; ++i) { std::cout << "正在做第" << i << "道题,加油!" << std::endl; } std::cout << "全部做完啦,休息一下吧!" << std::endl;
for循环的结构很紧凑,通常包含三个部分:初始化(比如int i = 1),条件判断(i <= 10),以及每次循环后要做的事(++i)。每次循环开始前都会判断条件,只有条件成立才会进入循环体。
do-while循环有点像“至少做一次,然后再决定要不要继续”。比如你去餐厅吃自助餐,至少要吃一盘菜,然后每吃完一盘都问自己“还要不要再来一盘”,只要你还想吃,就继续。
|char again; do { std::cout << "请享用一盘美食!" << std::endl; std::cout << "还要再来一盘吗?(y/n):"; std::cin >> again; } while (again == 'y' || again == 'Y'); std::cout << "吃饱啦,欢迎下次光临!" << std::endl;
do-while是无论你怎么选,至少会执行一次循环体。
有时候你会遇到“提前结束循环”的需求,比如老师说“谁先做完谁可以提前下课”。这时可以用break语句来打破循环。
|int answer; while (true) { std::cout << "猜猜我心里想的数字(1~10):" << std::endl; std::cin >> answer; if (answer == 7) { std::cout << "恭喜你猜对了!" << std::endl; break; // 猜对了,立刻结束循环 } else { std::cout << "没猜中,再试试!" <<
只要猜对了,break就会让循环立刻结束。注意我们在进行条件判断时,使用了true,代表这个循环会一直执行,除非我们手动结束。
有时候你希望“跳过本次循环,直接进入下一轮”,比如老师批改作业时,遇到空白卷就直接跳过,不批改,继续批下一个。
|for (int i = 1; i <= 5; ++i) { int score; std::cout << "请输入第" << i << "位同学的分数(-1表示缺考):" << std::endl; std::cin >> score; if (score == -1) { std::cout << "该同学缺考,跳过。" << std::endl;
遇到特殊情况时,continue可以让你灵活地控制循环流程。
goto语句可以让程序无条件跳转到指定的标签位置,就像在教室里突然喊“全体集合到操场!”。不过,goto容易让程序变得混乱,像迷宫一样难以维护,所以除非万不得已,一般不建议使用。
|int n = 1; start: std::cout << "第" << n << "次集合!" << std::endl; ++n; if (n <= 3) goto start; std::cout << "集合结束!" << std::endl;
虽然goto能实现跳转,但更推荐用循环和条件语句来表达你的逻辑。
在现实生活中,我们做事情时总会遇到一些“意外情况”,比如你在家做饭,突然停电了,或者你在网上购物,付款时网络断了。 这些意外如果不处理,可能会让整个流程中断,甚至造成更大的问题。C++也有类似的机制,叫做“异常处理”,用来优雅地应对程序运行中的各种突发状况。
C++的异常处理主要靠try和catch两个关键字。你可以把可能出错的代码放在try块里,如果真的发生了异常,程序会立刻跳到对应的catch块去处理。
举个例子,假如你让用户输入两个数做除法:
|try { double a, b; std::cout << "请输入被除数:"; std::cin >> a; std::cout << "请输入除数:"; std::cin >> b; if (b == 0) { throw std::runtime_error("除数不能为0!"); // 抛出异常 } std::cout << "结果是:"
在这个例子里,如果用户输入0作为除数,throw语句会抛出一个异常,程序会立刻跳到catch块,输出错误信息,而不是直接崩溃。
throw就像是“报警器”,一旦发现问题,立刻发出警报。你可以抛出各种类型的异常对象,比如字符串、整数、标准库里的异常类等。
|if (temperature < -273.15) { throw "温度不能低于绝对零度!"; }
你也可以抛出自定义的异常类型,让catch更有针对性。
catch就像是“接警员”,负责处理报警。catch后面要写上能接收异常的类型。你可以写多个catch,分别处理不同类型的异常。
|try { // ... } catch (const char* msg) { std::cout << "字符串异常:" << msg << std::endl; } catch (int code) { std::cout << "错误码:" << code << std::endl; } catch (...) { std::cout << "遇到未知异常!" << std
catch的顺序很重要,应该把具体类型的catch写在前面,最后用catch (...)兜底,防止漏网之鱼。
C++标准库提供了很多异常类,比如std::exception、std::runtime_error、std::out_of_range等。这些类都可以用来抛出和捕获异常,而且可以通过what()方法获取详细的错误信息。
|try { std::vector<int> v = {1, 2, 3}; std::cout << v.at(5) << std::endl; // 越界访问 } catch (const std::out_of_range& e) { std::cout << "越界错误:" << e.what() <<
你可以在catch块里再次抛出异常,让外层的catch来处理。这就像是小区保安发现问题,自己解决不了,就上报给警察。
|try { try { throw std::runtime_error("内部错误"); } catch (const std::runtime_error& e) { std::cout << "内部处理失败,转交外部:" << e.what() << std::endl; throw; // 重新抛出 } } catch (const std::exception&
现在让我们通过一些简单的练习题来巩固本章学到的知识。每道题都涵盖了语句和流程控制的核心概念,请先尝试独立完成,然后再查看答案。
编写一个程序,读取用户输入的年龄,然后根据年龄输出对应的阶段:
|#include <iostream> using namespace std; int main() { int age; cout << "请输入年龄:"; cin >> age; if (age < 18) { cout << "未成年" << endl; } else if (age <= 35) { cout << "青年"
编写一个菜单程序,用户输入1~4选择不同操作:
使用switch语句实现,并处理非法输入。
|#include <iostream> using namespace std; int main() { int choice; cout << "请选择操作:" << endl; cout << "1. 查询余额" << endl; cout << "2. 存款" << endl; cout << "3. 取款" << endl; cout << "4. 退出" << endl; cin >> choice;
编写一个程序,输出1到100之间所有能被7整除的数字。
|#include <iostream> using namespace std; int main() { cout << "1到100之间能被7整除的数字:" << endl; for (int i = 1; i <= 100; i++) { if (i % 7 == 0) { // 判断是否能被7整除 cout << i << " "
编写一个程序,循环读取10个整数,如果遇到负数则跳过(continue),如果遇到0则提前结束输入(break),最后输出所有有效输入的和。
|#include <iostream> using namespace std; int main() { int sum = 0; int num; cout << "请输入10个整数(遇到0会提前结束):" << endl; for (int i = 0; i < 10; i++) { cin >> num;
编写一个除法计算器,用户输入两个数,如果除数为0则抛出异常并用catch捕获,输出友好的错误提示。
|#include <iostream> #include <stdexcept> // 用于标准异常类型 using namespace std; int main() { double num1, num2; cout << "请输入被除数:"; cin >> num1; cout << "请输入除数:"; cin >> num2; try { if (num2 == 0) {
这道题展示了if-else if-else的链式结构。每个条件按顺序判断,一旦某个条件满足,就会执行对应的代码块并跳过后续的判断。注意条件的顺序很重要,应该从小到大或从大到小排列。
switch语句适合处理多个离散值的选择。每个case后面都要有break,否则会继续执行下一个case(这叫做"穿透")。default用于处理所有不匹配的情况。
for循环适合已知循环次数的情况。我们也可以直接从7开始,每次增加7:for (int i = 7; i <= 100; i += 7),这样更高效。使用取余运算符 % 可以判断一个数是否能被另一个数整除。
break 用于立即退出整个循环,continue 用于跳过本次循环的剩余部分,继续下一次循环。在这个例子中,负数被跳过不累加,遇到0则提前结束整个输入过程。
异常处理机制让我们可以优雅地处理错误情况。try 块中包含可能抛出异常的代码,catch 块用于捕获并处理异常。使用异常处理可以让程序在遇到错误时不会崩溃,而是给出友好的提示信息。