004_控制流
一、二路分支
核心逻辑:基于单一条件选择执行路径
if (条件)
{
// 条件为真时执行
}
else
{
// 条件为假时执行
}
关键点:
- else必须依附if:else不能独立存在。
- 大括号不可省略:即使只有一行代码,也需用{}明确作用域。
- 分号陷阱:if后误加分号(如if(条件);)会导致逻辑错误,无论条件是否成立均执行后续代码块。
- 常见应用:
- 输入验证(如判断数字奇偶性)
- 简单条件筛选(如触摸屏坐标范围检测)
示例问题:
if (num % 2 == 0); // 分号导致逻辑失效
{
printf("偶数"); // 无论num是否为偶数均会执行
}
二、多路分支
核心逻辑:根据多个互斥条件选择不同执行路径
两种实现方式:
- if-else if-else链:
if (条件1) { /* 代码1 */ }
else if (条件2) { /* 代码2 */ }
else { /* 默认代码 */ }
- 特点:灵活支持复杂条件表达式(如范围判断)。
- switch-case结构:
switch (整型表达式) {
case 常量1: 代码1; break;
case 常量2: 代码2; break;
default: 默认代码;
}
- 限制:
- case后必须为整型常量(如'A'或100),不能是变量或const修饰的“常量”。
- 缺少break会导致“穿透”执行后续case。
典型场景:
- 菜单选择(如指纹识别流程控制)
- 状态机实现(如界面跳转逻辑)
易错点:
const int CODE = 100;
switch (num) {
case CODE: // 错误!const变量不被视为编译期常量
case 100: // 正确
break;
}
三、const
核心作用:声明只读变量,防止意外修改
语法:
const int num = 100; // 推荐写法
int const num = 100; // 等效写法
关键规则:
- 初始化必须:const变量定义时需赋值。
- 非真常量:C语言中const仅为“只读”提示,仍可通过指针间接修改(与C++不同)。
- switch限制:case标签不接受const变量(因其非编译期常量)。
示例:
const int MAX = 100;
int *p = (int*)&MAX;
*p = 200; // 未定义行为!可能引发运行时错误
四、while循环
核心逻辑:先判断条件,后执行循环体(可能零次执行)
语法:
while (条件) { // 循环体 }
应用场景:
- 不确定次数的循环(如读取用户输入直到合法)
- 事件轮询(如嵌入式设备状态检测)
优化技巧:
- while(1)+break:替代do-while实现“至少执行一次”逻辑。
- 界面跳转:通过标志变量(如inf1_flag)控制多层循环嵌套。
对比for:
int i = 0; // 初始化
while (i < 10)
{ // 条件
printf("%d", i);
i++; // 更新
}
// 等效for循环:
for (int i = 0; i < 10; i++)
五、do-while循环
核心逻辑:先执行循环体,后判断条件(至少执行一次)
语法:
do {
// 循环体
} while (条件); // 注意结尾分号
独特价值:
- 输入验证:强制用户至少输入一次。
- 资源初始化:确保初始化代码至少运行一次。
六、for循环
核心逻辑:集初始化、条件检查、更新于一体
标准语法:
for (初始化; 条件; 更新)
{
// 循环体
}
灵活变体:
- 省略表达式:for (;;)等效while (1)。
- 逗号运算符:多变量控制(如for (i = 0, j = 10; i < j; i++, j--))。
底层开发注意:
- 某些嵌入式编译器要求循环变量在for外声明(如int i; for (i=0; ...))。
示例陷阱:
for (int i = 0; i < 5; i++); // 分号导致空循环
{
printf("%d", i); // 错误!i的作用域仅限于for循环
}
七、break和continue
使用场景:
- break:
- 提前终止循环(如找到目标元素时退出搜索)
- 避免switch穿透
- continue:
- 跳过无效数据(如处理数组时忽略负数)
- 优化循环效率(如仅处理偶数)
嵌套循环注意:break仅跳出当前层循环,如需跳出多层需配合标志变量。
八、goto语句
核心逻辑:无条件跳转到标签处
合理使用场景:
- 错误处理:集中释放资源(如goto cleanup;)。
- 深层嵌套退出:快速跳出多层循环(替代多个break)。
争议与规范:
- 反对理由:破坏结构化编程,降低可读性。
- 最佳实践:
- 限制跳转方向(只向前或只向后)
- 避免在循环和switch中使用goto
示例(错误处理):
if (open_file() == FAIL)
goto error;
if (read_data() == FAIL)
goto error; // ...正常流程...
error:
close_file();
return FAIL;
实验
【1】、实验1:(循环控制)
编写一个程序,输出26个小写字母。
解题思路:
- 字符变量初始化:从'a'开始,ASCII码值为97。
- 循环结构选择:
【2】、实验2:(循环控制)
编写一个程序,在用户输入某个大写字母后,产生一个金字塔图案。
例如:输入字母E,则产生如下图案:

提示:找规律
解题思路:
- 输入处理:读取大写字母(如'E'),计算层数(layers = ch - 'A' + 1)。
- 双层循环设计:
- 外层循环:控制行数(1到layers)。
- 内层循环:
- 空格:每行空格数为layers - current_row。
- 升序字母:从'A'输出到'A' + current_row - 1。
- 降序字母:从'A' + current_row - 2反向输出到'A'。
【3】、实验3:(循环控制、多路分支)
编写一个程序,接收用户的输入信息,当用户输入完成后('#'代表输入完成),自动统计用户输入的空格数、大小写字母数和其他字符数。
提示:scanf函数如何输入空格字符?
char buf[128] = {0};
scanf("%[^#]", buf); // 除非遇到#号,否则不退出输入界面