科技 · 编程
C++ 控制结构
从顺序、分支到循环结构,系统介绍 C++ 程序流程控制的基础语法与典型问题。

本系列为 C++ 的入门学习者服务,旨在于为此前无 C++ 基础的学习者简单介绍关于 C++ 语言基础的部分知识,如果已入门 C++,则不需要阅读本系列。
2.1 算法与流程图(不展开讲)
2.2 C++ 中的语句
语句是 C++ 程序的最基本单位,以分号结束。
注:头文件包含属于编译预处理,不是 C++ 语句。
#include <iostream> //头文件包含不是语句int a; //是语句1. 声明语句
用来定义或声明变量和函数。
int a, b;2. 表达式语句
完成计算和操作。
c = a + b;3. 控制语句
用来完成对程序流程的控制。
如:if 语句、for 语句、while 语句等。
4. 函数调用语句
用来完成函数的调用。
strcpy(a, b);5. 空语句
单独分号构成的语句。
意义:用在无内容可写,却需要语句来进行填充的位置。常见于循环语句,注意和分号区分。
6. 复合语句
由一对{}构成,用来包括多个语句。
- 形式:
{语句1; 语句2;}
if(i <= 100) //该段语句只是示例,无实际意义{ a = b + c;}- 意义:用在只需要一个语句,但单个语句无法满足需求。
- 注:在语法上复合语句认为是一条语句。
2.3 分支结构
1. if 语句
(1) 单分支 if 语句
if(表达式) 语句; 解释:表达式一般为逻辑或关系表达式,如果结果为 true,则运行语句。而如果表达式是其他类型,则自动转换,非0 为 true,0 为 false。

单分支 if 语句运行图示
(2) 双分支 if 语句
if(表达式) 语句1;else 语句2;解释:如果表达式结果为 true,则运行 语句1,反之运行 语句2。

双分支 if 语句运行图示
例:闰年判断(4 倍数一般是闰年,整百数必是 400 倍数才是闰年)
int year;cin >> year;if(year % 4 == 0 && year % 100 != 0 || year % 400 == 0) cout << year << “是闰年” << endl;else cout << year << “不是闰年” << endl;注:if 语句的嵌套
if 语句的分支语句可以是一个新的 if 语句,叫做 if 语句的嵌套。
借助 if 的嵌套可以实现多分支的判断功能。
A. 嵌套在 else 语句:阶梯式 if 语句
if(表达式) 语句1;else if(表达式2) 语句2;else if……else 语句n;解释:依次对各个表达式进行判断,如果有满足 true 的则执行对应语句,并终止整个语句。
(注意和 hoi4 伪语言区分开)

嵌套在 else 的 if 语句运行图示
B. 嵌套在 if 语句:在 if 分支语句中增加新的 if 语句(不推荐)
-
C++ 规定了
if和else的“就近配对”原则,即相距最近且还没有配对的一对if和else首先配对。if(表达式1) if(表达式2) 语句1; else 语句2;//else将与第二个if匹配。 -
如果需要强行更改配对关系,则要将属于同一层的语句放在
{}中。if(表达式1){ if(表达式2) 语句1;}else 语句2;//else将与第一个if匹配。
2. 条件运算符 ?:
三目运算符?:可以简化 if 语句表达。
表达式1 ? 表达式2 : 表达式3;解释:判定 表达式1,若成立则执行 表达式2,不成立则执行 表达式3。
注:
- 条件运算符中后两个表达式的类型和第一个表达式完全无关。
- 条件运算符等级仅高于赋值(
15)。 - 条件运算符是右结合性,自右向左结合。
- 例:
a ? b : c ? d : e应该理解为a ? b : (c ? d : e)
- 例:
3. switch 语句
switch(整型表达式){case 常量表达式1 :《语句序列1》《break;》// 《》是“可选”的意思……case 常量表达式n :《语句序列n》《break;》《default : 语句序列》}解释:
-
计算
switch后的整型表达式值。 -
逐个匹配
case后表达式的值,如果相等则执行对应的语句序列。 -
如果语句后有
break则终止整个switch语句,否则继续执行后续语句序列且不再进行判断。 -
若所有
case都无法匹配则执行default语句。
例:计算学生的成绩等级
int score;cout << "请输入学生的成绩:"; cin >> score;switch(score/10){case 10: case 9: cout << "该生成绩为A" << endl; break;case 8: cout << "该生成绩为B" << endl; break;case 7: cout << "该生成绩为C" << endl; break;case 6: cout << "该生成绩为D" << endl; break;default: cout << "该生成绩为E" << endl; }注:
switch语句只能对整数和字符类型使用。- 只能判断表达式是否相等,不能判定不等大于小于。
case后的表达式只能是常量表达式。break语句的使用请务必注意。
2.4 循环结构
1. while 语句
while(表达式) 循环体语句;解释:如果满足表达式则反复执行循环体语句直到不满足表达式为止。

while 语句运行图示
例:求解 1~100 的和
const int n=100; //用常变量利于修改程序int i=1, sum=0; //循环初始条件while(i <= n) //循环入口条件{ sum+=;//循环体 ++i;//循环修正条件}cout << "sum=" << sum << endl;解析:
-
代码结束后
i的值为101。 -
sum=0不能遗忘,否则会出现随机数。 -
做题看结果通常看头尾。
-
若循环体中有多个语句一定要用
{}。 -
部分题目中会将循环修正条件放入循环入口条件中,即将循环体中的
i++放入while(i++<=n)中,但一般不推荐这样写。
2. do-while 语句
do 循环体语句while(表达式);解释:先做一次循环体语句,若表达式满足则继续循环,直到表达式不满足终止循环;若表达式不满足则不执行循环。
注:do-while 语句与 while 语句的区别
do-while语句至少执行一次循环体后再判断循环条件是否满足。while语句先判断条件,然后才执行循环体,可能一次都不执行。
例:求解 1~100 的和
const int n=100;//用常变量利于修改程序int i=1, sum=O;//循环初始条件do{ sum+=i;//循环体 i++;//循环修正条件}while(i<=n);//循环入口条件cout << "sum=" << sum << endl;3. for 语句
for (表达式1; 表达式2; 表达式3) 循环体语句;解释:先执行式 1,然后判断式 2,真则执行循环体,后执行式 3,接着重复判断式 2,直到 2 为假结束整个循环。
注:
- 相对于 while,式 1 相当于初始条件;式 2 相当于维持条件;式 3 相当于修正条件。
- for 语句中只有式 2 是判定条件。
- for 语句中三个表达式都可以为空,但是分号**
;**一定要有。 - 省去 1 可以将初始条件放在
for之前,省去 3 可以将修正条件放在循环体中,而省去 2 则表达式 2 的值将默认为true,必须用其他方法结束循环。
例:求解 1~100 的和
const int n=100; //用常变量利于修改程序int i, sum=O;for(i=1;i<=n;++i) sum += i;cout << sum << endl;4. 三种循环的选择与注意
A.三种循环语句没有本质区别,可以互相代替。
B.当循环起点和终点值直接确定,推荐使用 for 语句。
C.若终止条件不明确(如等待某值输入),推荐 while 语句。
D.do-while 语句最典型场合是必须至少执行一次的循环。如展示菜单。
E.若循环外还要用某变量,则该变量不能在 for() 和 while() 语句中定义。
5. 循环语句的嵌套
当循环语句中的循环体中又有循环语句时,就构成了嵌套循环。
理解:以时钟的时针和分针为例:
时针每移动一格(大循环),分针就必须跑完一圈(小循环)。
解释:大循环每进行一次,小循环就完整循环一次。
注:大小循环的运行可能相关也可能不相关。
经典案例:打印问题
- 主要思想:利用循环算法,打印各种有规律的数字或图形。
- 基本思路:行是大循环,列是小循环。首先断定打印的行数,然后在每一行当中确定打印的列数以及每一列的空格和内容。行数、列数以及打印内容往往存在关联,需要分析清楚并用来控制循环的次数。
算法示例:

打印星号平行四边形
for (int i = 1; i <= 4;++i){ for (int j = 1; j <= i; ++j) cout << ' '; for (int j = 1; j <= 7; ++j) cout << '*'; cout << '\n';}解析:
-
首先确定行数为 4 行。
-
每行包括前面空格后面*号。
-
空格数量和行号相等。
-
*号的数量是固定为 7 个。
-
打印完每一行之后要换行。
6. 循环中的控制语句
(1) break 语句
- 在循环语句中遇到
break,整个循环结束。 - 如果有多层循环嵌套,则
break只结束最近的循环。
例:素数判断
基本思路:i 从 2 到 根号n 之间进行循环,将每个 i 用 n 去除,如果可以整除则直接判断不是素数;如果 i 的整个循环走完仍不能整除才能判断 n 是素数。
int n, k, i;cout << "输入整数n:" ;cin >> n;k = sqrt(1.0 * n);for (i = 2; i <= k; ++i) if (n % i == 0) break;if(i > k) cout << "是素数\n";else cout <<"不是素数\n";(2) continue 语句
在循环语句中遇到 continue,当轮循环停止,立即开始下一轮循环。
例:输出 100 以内的偶数。
for (i = 1; i <= 100; i++){if ( i % 2 != 0 ) continue; cout<< i << endl;}注:break 与 continue 的区别:
break 终止整个循环,而 continue 只是终止一次循环。
2.5 C++ 常用基本算法
1. 穷举法
- 主要思想:列出问题的所有可能性,按所需条件进行逐一筛选。
- 要求:在设计算法时,应尽可能减少循环的次数以提高效率。
(1) 素数问题
(2) 水仙花数问题
问题说明:如果一个三位数等于其各位数字的立方和,则该数为水仙花数。
问题思路:将所有的三位数用条件进行验证即可
代码:
int i, a, b, c; //a,b,c分别为百位、十位、个位for(i=100; i<=999; i++){ a = i/100; b = (i-a*100)/10; c = i - a*100 - b*10; if(i==a*a*a+b*b*b+c*c*c) cout << i << '\t'}(3) 完全数问题
问题说明:如果一个数等于其所有因子(自身除外)的和,则该数为完全数。
如:6=1+2+3,28=1+2+4+7+14
问题思路:遍历一个数的所有因子,并将其求和之后判断与自己是否相等即可。
问题要点:如何遍历一个数的所有因子?
A.单个数的判断:
int i, n, s=0; //注意s必须初始化为0cin >> n;for(i=1; i<=n/2; i++) if(n%i==0) s += i;if(s==n) cout << "该数是完全数\n";else cout << "该数不是完全数\n";B.遍历指定区间的完全数:(以 1000 以内为例)
- 未优化代码:正确但运算次数大,判断需要进行
n/2次循环。
int i, n, s;for(i=1; n<=1000; n++){ s = 0; //注意s必须在此处初始化,对每一个n,s都应该重置为0开始 for(i=1; i<=n/2; i++) if(n%i==0) s += i; if(s==n) cout << n << "\t";}- 代码优化:当区间过大的时候,将其从
2到根号n进行循环,每次求和的时候同时加上成对因子,但如果有重复的因子需去除。
int i, n, k, s=1; //因子必定有1,将s初始化为1减少一次循环cin >> n;k = sqrt(n);for(i=2; n<=k; i++) if(n%i==0) s += i + n/i; if(k*k == n) s -= k; //关键!!k为平方数时会出现一个重复因子i = n/i故要减一次 if(s==n) cout << "该数是完全数\n"; else cout << "该数不是完全数\n";(4) 简单推理问题
例:某同学做了好事但不留名。校长问了 4 个人:A 说不是我,B 说是 C,C 说是 D,D 说 C 胡说。已知 4 人中有 3 人说的是真话,请问做好事的是谁?
问题思路:遍历所有可能做好事的人,统计说真话的人是否为 3 个即可。
问题要点:如何用程序来计算和统计本问题的真假?
char man;for(man = 'A'; man <='D'; man++){if((man!='A')+(man=='C') +(man=='D')+(man!='D')== 3 ) //3个bool=1cout<<"好人就是"<<man<<endl;}2. 递推法
- 主要思想:按照某种规律,对同一个变量不断更改其值(递推),从而求解问题。
- 基本思路:确定递推项 → 确定递推起点 → 分析递推规律 → 判断终止条件
(1) 斐波那契数列
数列背景︰
数列
1,1,2,3,5,8…为以意大利数学家费波纳切命名的数列。 定义小兔子t时间后长为大兔子,大兔子会繁殖且繁殖周期为t。 则 1 对小兔子t时间后长大为 1 对大兔子。t时间后变成 2 对(1 对小 1 对大)。t时间后变成 3 对(2 对大 1 对小)。t时间后变为 5 对(3 对大 2 对小)等等。
问题背景:现用循环求出该数列的前 n 项。
问题思路:不可能定义 n 个变量逐个保存,只能用少数的变量反复使用来达到该效果。
问题要点:如何找出递推的变化规律?务必通过该问题,掌握递推算法的核心思想。
斐波那契数列递推思路:
-
确定递推项:三项
f1,f2,f3 -
递推起点:
f1=f2=1 -
递推规律:
f3=f1+f2,然后旧f2推出新f1,旧f3推出新f2,再重复前述过程例:(右上到左下替换)
f1 f2 f3 1 1 2 1 2 3 2 3 5 -
终止条件:指定次数(本例为
n-2次)
解法一:
int f1=1, f2=1, f3, i; //只定义3个变量cout << f1 << '\t' << f2 << '\t'; //打印开始for(i=3; i<=20; i++) //打印前20个值{ f3 = f1 + f2; //打印结果 cout<<f3<<'\t'; if(i%5==0) //每5个数换一行 cout << endl; f1 = f2; //变量递推,准备下一次的值 f2 = f3; //变量递推,顺序问题很重要解法二:
递推规律:新f1=旧f1+旧f2;新f2=新f1+旧f2
| 旧 f1 | 旧 f2 | 新 f1 | 新 f2 |
|---|---|---|---|
| 1 | 1 | 2 | 3 |
int f1=1, f2=1, i;for(i=1; i<=10; i++){ cout << f1 << '\t' << f2 << '\t'; f1 = f1 + f2; f2 = f1 + f2;} //请对照递推规律自行理解体会该思路(2) sin(x)的求解问题
已知 sin(x) = x - x^(3) / 3! + x^(5) / 5! - x^(7 )/ 7! + .....
利用该公式求解 sin(x) 的值,精度为 10^(-6)。
问题思路:观察每一项的分子、分母和符号位的值。当前项的符号位乘 -1 就是下一项的符号位;当前项的分子乘 x^(2) 就是下一项的分子;当前项的分母乘以 (2*n+1)*(2*n),即为下一项的分母,n 为当前的项数。
精度为 10^(-6)可用来控制循环的终止,如果计算的通项绝对值小于该值,则循环终止。
示例:求 sin 3 = ?
double x = 3, s = 0, t = x;int n = 1;while(fabs(t) >= 1e-6){ s += t; t = -t * x * x / (2 * n + 1) / (2 * n); ++n;}cout << s << endl;(3) 二分法求解方程问题
问题说明:假设 f(x) 在区间 [a,b] 中单调递增,且肯定有根,求解该根的值。
问题思路:取区间的一半判断其函数值,如果为负,则收缩区间为中点到右区间,反之为左区间到中点。重复上述过程,直到中点值为 0 或满足精度要求即可。
问题要点:反复递推区间的左右值即可,当求出的中点值小于某区间即可终止。
示例:二分法求 x^(2)-2=0
double a=0,b=2,c=(a+b)/2; //递推起点double t = c*c-2;while(fabs(t)>=1e-6)//终止条件 不能写t!=0,因计算机中t实际不能达到精确的0{ if(t>0) b=c; //递推过程1 else a=c; //递推过程1 c=(a+b)/2; //递推过程2 t = c*c-2;}cout << c << endl;(4) 整数分解问题
问题说明:输入任意正整数,判断其位数、计算各位数字之和、求解其倒序数、判断是否为回文数等问题。
问题思路:从个位数开始依次提取,其方法是对 10 求余,然后将该数除以 10,并重复上述步骤,直到该数为 0 则结束。
问题要点:上述思路仅限分解,如果是组装则逆向进行。
示例:求解 12345 的倒序数。
int n = 12345,m = 0;do{ m = m * 10 + n % 10; //n%10求解末位数,n/=10将末位向左移一位从而循环 n /= 10;} while (n > 0);cout << m << '\n';3. 模拟法
基本思想:根据题目给出的规则,对题目要求的相关过程进行模拟。模拟法对于步骤比较繁杂的问题比较合适。
报数问题
题目:小张按 1~20 报数,小李按 1~30 报数,若两人同时开始,并以同样速度报数,当两人都报了 1000 个数时,报相同数字的次数有多少?
思路:进行 1000 次循环,每一次小张和小李各报一次数(注意调整下一次的值),统计相等次数即可。
int round, zhang = 0, wang = 0, count = 0;for (round = 1; round <= 1000; ++round){ ++zhang; ++wang; if(zhang == wang) ++count; if(zhang == 20) zhang = 0; if(wang == 30) wang = 0;}cout << count;