
02-C语言-控制篇
本文最后更新于 2025-03-11,学习久了要注意休息哟
第一章 程序流程控制概述
1.1 顺序结构
📌 三大特征
- ➡️ 单线程执行:代码像流水线一样逐行运行
- ⏩ 不可跳跃:
A→B→C
严格顺序 - 🧩 基础结构:所有程序的默认执行方
🖥️ 代码演示
#include <stdio.h>
int main() {
int a = 10; // 🎯 STEP 1
int b = a * 2; // ⚙️ STEP 2
printf("结果:%d", b); // 🖨️ STEP 3
return 0; // 🏁 结束
}
📊 流程图表示
开始 → [操作1] → [操作2] → [操作3] → 结束
▼ ▼ ▼
输出a 计算b 打印结果
1.2 选择结构
🔀 分支决策剧场
🧮 结构对比表
if-else | switch-case | |
---|---|---|
图标 | 🔍条件检测 | 🔢数值匹配 |
优势 | 处理复杂逻辑 | 多分支效率高 |
局限 | 嵌套过多易混乱 | 仅支持整型 |
⚠️ 经典错误
if(a = 5){...} // ❌ 错误!应使用 ==
// 💡 建议:if(5 == a) 防错写法
1.3 循环结构
🔁 循环三剑客
while循环:🤔条件→🔄执行
do-while:🔄执行→🤔条件
for循环:🔢计数循环专家
📈 性能对比
// 🐢 低效写法
int i=0;
while(i<10){
printf("%d",i);
i++;
}
// 🐇 高效写法
for(int i=0;i<10;i++){
printf("%d",i);
}
1.4 结构化程序设计原则
🚫 三大禁忌
- 🍝 意大利面条代码:避免无节制的goto
- 🕳️ 黑洞函数:拒绝超过50行的代码块
- 🧩 碎片化逻辑:杜绝重复代码段
✅ 优质代码特征
清晰结构 → 🏗️ 模块化设计
可读性强 → 📖 见名知意变量
易于维护 → 🛠️ 低耦合高内聚
📚 复习加油站
🧮 速记口诀
顺序结构一条线,选择结构分两边
循环结构转圈圈,结构清晰好维护
🚫 错题本模板
错误现象 | 原因分析 | 解决方案 |
---|---|---|
死循环 | 条件未更新 | 检查循环变量 |
分支失效 | 条件覆盖不全 | 补全else分支 |
这种设计方案的优点:
💻 兼容性强:所有平台完美显示
🎨 视觉层次:通过符号/缩进/分割线构建阅读节奏
📱 移动友好:无图片加载压力
✏️ 易于修改:直接复制粘贴即可调整样式
第二章 条件判断语句
🔍 本章目标:掌握程序分支决策的核心机制
2.1 if语句
🧱 结构框架
// 单分支结构
if(条件) {
// 🎯 条件为真时执行
}
// 双分支结构
if(条件) {
// ✅ 真分支
} else {
// ❌ 假分支
}
// 多分支阶梯
if(条件1) {
// 📌 情况1
} else if(条件2) {
// 📌 情况2
} else {
// 🧩 默认情况
}
🚨 经典错误集
错误代码 | 问题分析 | 修正方案 |
---|---|---|
if(a = 5) | 赋值替代比较 | if(5 == a) |
if(1 < a < 5) | 逻辑表达式错误 | if(a>1 && a<5) |
嵌套超过3层 | 可读性降低 | 改用switch或函数封装 |
🔄 执行流程图
graph LR
A[条件判断] -->|Yes| B[代码块A]
A -->|No| C[代码块B]
💡 实战技巧
-
防御性写法:
if(5 == a) // ✅ 常量放左侧,避免写成赋值
-
简化嵌套:
// 嵌套写法 → 卫语句优化 if(error) return; // 🛑 提前返回 // 正常流程代码
2.2 switch-case语句
🎛️ 标准结构
switch(表达式) { // ⚠️ 仅支持整型/枚举
case 值1:
// 操作1
break; // 🛑 必须阻断穿透
case 值2:
// 操作2
break;
default: // 🧩 默认处理
// 未知情况
break;
}
⚔️ vs if-else 对比战
特性 | switch-case | if-else |
---|---|---|
匹配方式 | 精确值匹配 | 范围/逻辑判断 |
执行效率 | 跳转表直接定位 | 顺序判断 |
可读性 | 多分支时更清晰 | 简单分支时直观 |
限制条件 | 仅支持整型 | 无类型限制 |
💥 穿透现象演示
switch(level) {
case 3:
printf("奖励皮肤"); // 🎮 会继续执行case2
case 2:
printf("奖励金币"); // 💰 无break继续穿透
case 1:
printf("再接再厉"); // 🎯 最终输出三句话
break;
}
// 💡 合理利用穿透:多条件共享代码块
📜 行业规范
default必须存在:即使当前用不到
case排序规则:
- 数字:从小到大
- 字符:字母顺序
- 特殊case置顶
注释要求:
case VIP_USER: // 会员用户特权处理
🧪 综合训练营
案例1:成绩等级判断
if(score >= 90) {
grade = 'A';
} else if(score >= 75) {
grade = 'B';
} else if(score >= 60) {
grade = 'C';
} else {
grade = 'D';
}
案例2:工作日判断
switch(weekday) {
case 1: case 2: case 3: case 4: case 5:
printf("工作日 💼");
break;
case 6: case 7:
printf("休息日 🏖");
break;
default:
printf("非法输入 🚨");
}
📌 速记口诀
if判断范围强,switch精准值考量
break阻断穿透流,default保平安
🧠 脑图总结
graph TD
A{{条件判断}} -->|分支逻辑| B[if家族]
A -->|离散匹配| C[switch-case]
B --> B1[/单分支/]
B1 -->|条件过滤| D[简单过滤]
B --> B2{双分支}
B2 -->|true| E1[执行块A]
B2 -->|false| E2[执行块B]
B --> B3[[多分支]]
B3 --> F1[条件1]
B3 --> F2[条件2]
B3 --> F3[...n]
C --> C1>case穿透]
C1 -->|未break| G1[连续执行]
C1 -->|需break| G2[中断流程]
C --> C2(整型限定)
C2 --> H1[字符类型]
C2 --> H2[枚举类型]
classDef logic fill:#E1F5FE,stroke:#039BE5;
class B,B1,B2,B3,C,C1,C2 logic;
第三章 循环控制语句
🔄 本章目标:掌握重复执行代码的核心方法与优化策略
3.1 while循环
🎯 核心特征
while(循环条件) {
// 条件为真时重复执行
// 🔄 必须包含条件更新语句
}
⚡ 执行流程图
graph TD
A{条件判断为真} -- 是 --> B[执行循环体]
B --> C[更新条件]
C --> A
A -- 否 --> D[结束循环]
🚨 典型错误
int i=0;
while(i < 5) { // ❌ 缺少条件更新
printf("%d", i);
}
// 死循环警告!💀
💡 实战技巧
// 读取直到合法输入
while(scanf("%d",&n)!=1 || n<0){
printf("输入错误,请重试:");
while(getchar()!='\n'); // 清空输入缓冲区
}
3.2 do-while循环
🎯 核心特征
do {
// 至少执行一次
// 🔄 结尾必须带分号
} while(循环条件);
⚡ vs while对比表
特性 | while | do-while |
---|---|---|
执行顺序 | 先判断后执行 | 先执行后判断 |
适用场景 | 0~N次执行 | 1~N次执行 |
代码示例 | 文件读取 | 菜单交互系统 |
💡 实战案例
char choice;
do {
printf("1.开始游戏\n2.设置\n3.退出\n");
scanf(" %c", &choice);
handleChoice(choice); // 🎮 处理用户选择
} while(choice != '3'); // 🏁 输入3退出
3.3 for循环
🧱 标准结构解析
for(初始化; 条件; 更新){
// 循环体
}
// 等效while写法:
初始化;
while(条件){
循环体;
更新;
}
🔄 变形用法
for(;;){...} // ✅ 合法无限循环
for(int i=0,j=10; i<j; i++,j--){...} // 多变量控制
for(printf("Start"); i<5; i++){...} // 初始化为输出语句
📊 性能优化建议
// 优化前:每次循环计算长度
for(int i=0; i<strlen(s); i++){...}
// 优化后:预先计算长度
int len = strlen(s);
for(int i=0; i<len; i++){...}
3.4 循环嵌套
🎯 核心原则
for(int i=0; i<外循环次数; i++){ // 🌍 外层控制大流程
for(int j=0; j<内循环次数; j++){// 🔍 内层处理细节
// 注意变量作用域!
}
}
💥 经典错误案例
for(int i=0; i<3; i++){
for(int i=0; i<5; i++){ // ❌ 内外层变量同名
printf("*");
}
}
// 实际输出:***** ***** *****
💡 实战案例:乘法表
for(int i=1; i<=9; i++){
for(int j=1; j<=i; j++){
printf("%dx%d=%-2d ", j, i, i*j);
}
printf("\n"); // 🧮 打印完一行换行
}
🧠 脑图总结
循环结构
├─ while → 先判断后执行
├─ do-while → 先执行后判断
├─ for → 精确控制循环次数
└─ 嵌套循环 → 矩阵/多维数据处理
├─ 外层控制行 → 内层控制列
└─ 注意变量作用域
📌 速记口诀
while先判后执行,do-while保底行
for循环三要素,嵌套注意作用域
🧪 综合训练营
案例1:素数判断
for(int num=2; num<=100; num++){
int isPrime = 1;
for(int i=2; i*i<=num; i++){
if(num%i == 0){
isPrime = 0;
break;
}
}
if(isPrime) printf("%d ", num);
}
案例2:菱形打印
int n = 5;
// 上半部分
for(int i=1; i<=n; i++){
for(int j=1; j<=n-i; j++) printf(" ");
for(int j=1; j<=2*i-1; j++) printf("*");
printf("\n");
}
// 下半部分(略)
第四章 控制语句进阶
4.1 break语句
🎯 核心功能
// 在循环中立即终止当前层循环
while(1){
if(条件) break; // 🛑 紧急出口
// ...
}
// 在switch中阻断case穿透
switch(n){
case 1: ... break;
case 2: ... break;
}
⚠️ 使用规范
-
禁止在多重循环中跨层break(C语言不支持)
-
推荐配合标志变量使用:
int found = 0; for(...){ if(...){ found = 1; break; } }
4.2 continue语句
🔄 执行流程
┌───────────┐
│ 循环开始 │
└─────┬─────┘
▼
┌───────────┐
│ 条件判断 │←────┐
└─────┬─────┘ │
true │ false │
▼ ▼ │
执行循环体 → [continue]
│ │
└───────────┘
💡 实战案例
for(int i=0; i<100; i++){
if(i%2 == 0) continue; // 跳过偶数
processOdd(i); // 仅处理奇数
}
4.3 goto语句
⚠️ 争议与规范
// 🚫 典型滥用案例
goto label;
... // 复杂逻辑
label:
// ✅ 允许使用场景
for(...){
for(...){
if(错误发生)
goto cleanup; // 跳转到资源释放
}
}
cleanup:
free(resources);
🚨 行业规范
- 跳跃方向:只允许向前跳转
- 跳跃范围:禁止跨函数跳转
- 命名规范:标签用全大写+下划线(如
ERROR_HANDLE
)
4.4 return语句
📦 多返回值处理
// 通过指针参数返回多个值
int parseInput(char* str, int* outVal){
if(错误) return -1; // 🚩 返回状态码
*outVal = 计算结果;
return 0;
}
// 调用方
int result;
if(parseInput("123", &result) != 0){
// 错误处理
}
⏳ 资源释放责任链
void func(){
FILE* fp = fopen(...);
if(!fp) return; // ❌ 忘记关闭文件
// 正确写法
if(!fp) goto CLEANUP;
...
CLEANUP:
if(fp) fclose(fp);
}
4.5 exit语句
⚡ 执行特性
#include <stdlib.h>
if(致命错误){
fprintf(stderr, "系统不可恢复错误");
exit(EXIT_FAILURE); // 🔚 立即终止进程
}
🆚 exit与return对比
return | exit | |
---|---|---|
作用范围 | 退出当前函数 | 终止整个进程 |
清理动作 | 自动释放栈内存 | 跳过未执行的清理代码 |
使用场景 | 正常函数返回 | 不可恢复的系统级错误 |
🧪 综合训练营
案例1:用户登录尝试限制
for(int i=0; i<3; i++){
if(验证密码()){
printf("登录成功!🎉");
break;
}else{
if(i==2){
printf("账户锁定!🔒");
exit(EXIT_FAILURE);
}
continue; // 继续下一次尝试
}
}
案例2:资源泄漏风险
void dangerFunc(){
int* mem = malloc(1024);
if(错误发生){
exit(1); // 💥 内存泄漏!
}
free(mem);
}
📌 速记口诀
break断当前循环,continue跳下次忙
goto争议需慎用,return回家exit亡
🧠 脑图总结
流程控制进阶
├─ break → 循环/switch阻断
├─ continue → 跳过本次迭代
├─ goto → 争议性跳转工具
├─ return → 函数返回出口
└─ exit → 进程强制终止
├─ 风险:资源泄漏
└─ 适用:不可恢复错误
第五章 综合应用与调试
5.1 典型算法实现
1. 累加/累乘算法
// 累加(1~100求和)
int sum = 0;
for(int i=1; i<=100; i++) {
sum += i; // GDB调试点:观察sum变量变化
}
// 累乘(5!计算)
int product = 1;
for(int i=1; i<=5; i++) {
product *= i;
}
GDB调试命令:
(gdb) break 3 # 在循环体设置断点
(gdb) print sum # 查看累加值
(gdb) watch product # 监控乘积变化
(gdb) continue # 继续执行直到下一个断点
2. 最大/最小值查找
int arr[] = {3,9,2,5,1};
int max = arr[0];
for(int i=1; i<5; i++){
if(arr[i] > max) { // 断点位置
max = arr[i];
}
}
GDB调试技巧:
(gdb) break 4 if i==3 # 当i=3时触发断点
(gdb) x/5w arr # 查看数组内存布局
(gdb) display max # 持续显示最大值变量
3. 数字逆序处理
int num = 1234, reversed = 0;
while(num != 0) { // 注意此处错误!
reversed = reversed*10 + num%10;
num /= 10;
}
GDB排错流程:
- 编译时加
-g
选项:gcc -g reverse.c
- 启动调试:
gdb a.out
- 定位错误:
(gdb) run # 运行程序报错
(gdb) backtrace # 查看错误调用栈
(gdb) list # 显示错误代码位置
(gdb) break while循环行号 # 设置断点
(gdb) print num # 观察变量异常值
5.2 调试技巧
📦GDB核心命令速查表
命令 | 功能说明 | 示例 |
---|---|---|
break [行号/函数名] | 设置断点 | break main |
watch 变量名 | 监控变量写操作 | watch sum |
next (n) | 单步跳过函数调用 | n |
step (s) | 单步进入函数内部 | s |
info locals | 显示当前栈帧的局部变量 | info locals |
frame | 切换调用栈帧 | frame 2 |
x/[数量][格式] 地址 | 查看内存数据 | x/10dw &array |
💡 实战调试流程
-
启动调试
gcc -g program.c -o program gdb program
-
定位段错误
(gdb) run (gdb) backtrace # 查看崩溃时的调用栈 (gdb) frame N # 切换到问题栈帧 (gdb) print 可疑指针 # 检查指针地址合法性
-
追踪循环变量
(gdb) break 10 if i==5 # 第5次循环时暂停 (gdb) display j # 每次暂停显示j的值 (gdb) continue # 持续执行
5.3 常见错误分析
1. 分号位置错误
for(int i=0; i<5; i++); // ❌ 多写分号导致循环体为空
{
printf("Hello"); // 实际只执行一次
}
GDB验证:
(gdb) disassemble # 查看汇编代码
(gdb) stepi # 单步执行机器指令观察循环次数
2. 条件表达式错误
if(score = 60) { // ❌ 赋值替代比较
printf("及格");
}
GDB检测:
(gdb) watch score # 监控变量修改
(gdb) run # 触发条件时暂停
(gdb) print score # 发现值被意外修改
3. 循环控制变量错误
for(int i=0; i<=5; i++) { // ❌ 循环6次越界
arr[i] = i; // 当i=5时越界
}
GDB内存检查:
(gdb) break 2
(gdb) x/6w arr # 显示数组内存区域
(gdb) print &arr[5] # 确认数组边界
4. 作用域理解偏差
int i=5;
for(int i=0; i<3; i++){ // 内层i覆盖外层
printf("%d",i); // 输出0,1,2
}
printf("%d",i); // 输出5(外层i)
GDB验证:
(gdb) info locals # 查看不同作用域变量
(gdb) break 4 # 在循环外设置断点
(gdb) print i # 显示外层变量值
📌 调试口诀
编译必加-g选项,断点监控是核心
next跳过step进,内存查看用x命令
段错误先查指针,逻辑错要跟变量
🧠 脑图总结
综合调试
├─ 算法实现 → 理解执行流程
├─ GDB工具 → 掌握核心命令
└─ 错误分析 → 定位修正技巧
├─ 语法错误 → 编译器警告
└─ 逻辑错误 → 断点跟踪
第六章 项目实战
6.1 学生成绩管理系统(菜单驱动)
6.2 简易ATM机交互程序
6.3 数字猜谜游戏
6.4 质因数分解工具
6.5 N位数拆分
6.6 二进制转换
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 小道士