第一章 C++ 程序结构与流程控制
在准备 CSP 和 GESP 的过程中,理解 C++ 的程序结构和流程控制是编写算法的基础。C++ 程序由一系列指令构成,通过合理的顺序、选择和循环结构,控制计算机高效地执行任务。
下面是我们编写的第一个 C++ 程序,用于计算两个整数的和(A+B 问题):
#include <iostream>
using namespace std;
int main() { // 程序入口
int a, b;
cin >> a >> b;
cout << a + b << "\n";
return 0;
}
1.1 程序基本框架
1. 注释
注释用于提高代码的可读性,编译器会直接忽略它们。C++ 支持两种注释方式:
* 单行注释 //:必须单独放置在一行,或紧跟在代码行的后面。
* 块注释 /* ... */:成对出现,可以插入到代码的任意位置,常用于注释多行代码。
2. 引用头文件
在代码开头使用 #include <头文件名> 引入标准库。
* 尖括号 < >:表示编译器只从系统标准目录中搜索该头文件(竞赛中绝大多数情况使用此方式)。
* 双引号 "":表示编译器会先从当前源文件所在的目录搜索,如果找不到,再到系统标准目录中搜索(常用于引入自己编写的头文件)。
3. 命名空间
标准 C++ 库中的所有标识符都定义在一个名为 std 的命名空间中。为了避免每次都写 std::cin 或 std::cout,我们通常在头文件引入后加上:
using namespace std;
4. main() 主函数
main() 是所有 C++ 程序的入口。在算法竞赛(如 CSP-J、NOIP、GESP 考级)中,main() 函数的返回值必须是 0(通过 return 0; 返回)。如果返回其他非零值,评测系统会判定为程序异常终止(Runtime Error),从而导致该测试点得零分。
5. 语句与语句块
- 语句:在 C++ 中,单条语句必须以分号
;结束。为了代码的美观与可读性,一条语句可以换行书写,多条语句也可以写在同一行。 - 语句块:使用大括号
{和}包围的多条语句。在语法上,一个完整的语句块被视为单条复合语句。
1.2 选择结构(分支结构)
1. if 条件分支语句
if 用于判断条件是否为真。如果条件为真,则执行紧跟其后的语句或语句块;否则执行 else 分支中的代码。如果没有编写 else 且条件为假,则直接跳过该分支。
格式一:基础单分支
if (条件) {
语句或语句块;
}
格式二:双分支
if (条件) {
语句或语句块A;
} else {
语句或语句块B;
}
格式三:多分支(嵌套)
if (条件1) {
语句或语句块A;
} else if (条件2) {
语句或语句块B;
} else {
语句或语句块N;
}
- 注意:
else if中间必须留有空格。它在本质上是else分支中嵌套了另一个if语句。
2. switch 开关分支语句
switch 语句根据表达式的整数值(或字符值)进行多路分支选择,结构如下:
switch (表达式) {
case 值1:
代码段A;
break;
case 值2:
代码段B;
break;
default:
代码段N;
break;
}
break关键字:如果某个case分支中省略了break,程序会继续执行其后的所有case分支代码(产生“穿透”现象),直到遇到break或switch语句结束。default分支:当所有case都不匹配时执行default段,此部分可以省略。- 结束分号:整个
switch大括号闭合后不需要在结尾补分号(非强制,但保持代码整洁)。
实例:闰年判定与月份天数计算
输入一个日期,判断其所在年份是否为闰年,并输出该月份的天数。 (提示:闰年的判断方法为:四年一闰,百年不闰,四百年又闰。)
int year, month, day;
bool is_leap = false;
cin >> year >> month >> day;
// 判断是否为闰年
if (year % 400 == 0) {
is_leap = true;
} else if (year % 100 != 0 && year % 4 == 0) {
is_leap = true;
}
if (is_leap) {
cout << year << "是闰年。\n";
} else {
cout << year << "不是闰年。\n";
}
// 判断所在月份的天数
switch (month) {
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
cout << "这个月有31天。\n";
break;
case 4: case 6: case 9: case 11:
cout << "这个月有30天。\n";
break;
case 2:
cout << "这个月有" << (is_leap ? 29 : 28) << "天。\n";
break;
}
1.3 循环结构
1. while 语句
先判断条件,若条件成立则执行循环体,重复此过程直到条件不成立为止。
while (条件) {
循环体;
}
2. do-while 语句
先执行一次循环体,再判断条件。若条件成立则继续循环。它与 while 的主要区别在于:do-while 的循环体至少会被执行一次。
do {
循环体;
} while (条件); // 注意:结尾的分号不可省略
3. for 语句
for 循环将循环的控制结构集中在一行,包含:初始化、循环判定条件、更新变量(状态转移)以及循环体。
for (初始条件; 继续循环的条件; 状态转移的条件) {
循环体;
}
其等价的 while 循环形式为:
初始条件;
while (继续循环的条件) {
循环体;
状态转移;
}
for语句括号内的三个表达式都可以省略,但两个分号;必须保留(如for (;;)构成无限循环)。
4. 循环控制:break 与 continue
break:强行终止并跳出当前所在的最内层循环。continue:跳过当前循环体中剩余的语句,直接进入下一次循环判定或更新变量阶段。
5. 编写循环时的避坑指南
- 计数器变量冲突:小心拼写,特别是在多重循环复制粘贴代码时,切勿混淆循环变量(如将
j误写为i)。 - 边界条件:要明确关系运算符是
<、>还是<=、>=。 - 逆序死循环:在进行逆序循环(递减)时,切勿将自减
--误写为自增++。
实例:求多个数的阶乘值
读入多个正整数 $n$,输出 $n!$ ($n! = 1 \times 2 \times 3 \times \dots \times n$)。当输入为负数时,结束程序。
int n;
long long r = 1;
cin >> n;
while (n > -1) {
r = 1;
for (int i = 1; i <= n; i++) {
r *= i;
}
cout << n << "! = " << r << "\n";
cin >> n;
}
1.4 goto 语句
goto 语句用于无条件跳转。使用时,需先在代码中定义一个标签(标识符后加冒号 :),然后通过 goto 标签名; 跳转。
在结构化编程中,一般不建议过多使用 goto,因为它容易破坏程序的结构、降低可读性。但在某些特定场景下,goto 具有独特的作用,例如快速跳出多重循环:
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
for (int k = 0; k < 9; k++) {
if (满足某种跳出条件) {
goto __exited; // 直接跳出三重循环
}
}
}
}
__exited:
// 执行跳转后的后续代码
1.5 C 语言与 C++ 的重要区别
在算法竞赛中,C++ 是基于 C 语言发展而来的,绝大多数 C 语言代码可以直接在 C++ 编译器中运行。以下是两者在竞赛中的主要区别:
- 输入与输出:C++ 推荐使用灵活的流输入输出
cin/cout,而 C 语言仅支持scanf/printf。 - 字符串处理:C++ 提供了强大的标准库
std::string类,极大地简化了字符串操作,避免了传统 C 风格字符数组容易发生的越界、溢出等低级内存错误。 - 标准模板库(STL):C++ 拥有极其强大的 STL(包含
vector,queue,stack,set,map等数据结构及sort等算法),而 C 语言不提供这些工具,需要手写。 - 头文件命名:C 语言的传统头文件可以在 C++ 中使用,但建议规范写成去
.h并在前端加c的形式(例如<stdio.h>写作<cstdio>,<stdlib.h>写作<cstdlib>)。 - 运行效率:原生 C 语言的运行效率在部分场景下略优于 C++。但在通过
ios::sync_with_stdio(false)取消同步后,C++ 的流输入输出效率能与 C 语言基本持平。
1.6 课后推荐练习题
为了熟悉 C++ 的程序基本框架、选择结构与循环结构,推荐在工作台中练习以下题目:
- 题目编号:1 —— 第一个C++程序与A+B
- 考点:熟悉 C++ 最基础的程序框架、头文件引用、命名空间以及基本的输入输出流程。
- 题目编号:5 —— 鸡兔同笼
- 考点:练习基本的算术运算与
if-else条件选择结构。在计算出不可能的组合时,需要进行特殊的分支处理。
- 考点:练习基本的算术运算与
- 题目编号:25 —— 倍数判断
- 考点:练习关系运算符(余数等于 0)和逻辑或运算符
||。通过分支结构输出对应的字符串结果。
- 考点:练习关系运算符(余数等于 0)和逻辑或运算符
- 题目编号:33 —— 城市名称查询
- 考点:本题提供了清晰的映射关系。非常适合用来练习编写多级
if-else if嵌套结构或switch-case分支语句。
- 考点:本题提供了清晰的映射关系。非常适合用来练习编写多级
- 题目编号:4 —— 所有约数之和
- 考点:练习基础的
for循环。通过循环遍历1到N之间的每一个整数,利用条件分支判断其是否为约数并累加。
- 考点:练习基础的
- 题目编号:46 —— 递增序列输出
- 考点:练习使用
while循环读入数据。要求读取一系列整数,并对每一个非零数据通过for循环输出对应的递增序列,直到读入0时终止程序。
- 考点:练习使用
- 题目编号:56 —— 等差数列与终止条件
- 考点:练习循环中的输入与控制。在读取每对数据的同时需要进行多重条件判断,一旦读入
0或负数则需要立即跳出循环结束程序。
- 考点:练习循环中的输入与控制。在读取每对数据的同时需要进行多重条件判断,一旦读入
—— 本文来自火龙信奥(义乌睿码科技):义乌青少年信息学奥赛与编程教育平台,专注 CSP-J/S、NOIP、GESP 竞赛培训,线上线下融合教学,助力编程升学。网址:hlcoding.com