三、C语言流程控制:分支与循环

思维导图



一、 if/else:判断条件的本质

1.1 条件的真与假

核心概念:

在 C 语言中,没有原生的布尔类型。计算机判断真假的规则极其简单粗暴:非 0 即真,0 即假

这意味着,if (1)if (-5)if (3.14) 都会被视为真,只有 if (0) 才是假。

语法结构:

c 复制代码
if (表达式) {
    // 表达式为真(非0)时执行
} else if (表达式2) {
    // 表达式2为真时执行
} else {
    // 所有条件都不满足时执行
}

1.2 逻辑短路的妙用

机制:

对于 A && B,如果 A 为假 (0),计算机绝对不会去计算 B,因为结果注定是假。

对于 A || B,如果 A 为真 (非0),计算机绝对不会去计算 B,因为结果注定是真。

应用场景:

防止除以零或空指针解引用。

代码示例:

c 复制代码
int denominator = 0;
int x = 100;
// 如果不利用短路,这里会发生除零错误导致程序崩溃
if (denominator != 0 && (x / denominator) > 1) {
    printf("计算有效");
} else {
    printf("分母不能为零");
}

1.3 悬空 else陷阱

规则:

C 语言规定:else 总是与最近的、未匹配的 if 配对。缩进不能改变逻辑,大括号 {} 才能。

错误示例:

c 复制代码
if (a > 0)
    if (b > 0)
        printf("A和B都大于0");
else // 这里的缩进是骗人的!这个 else 属于 if(b>0)
    printf("A不大于0?错!其实是B不大于0");

修正: 永远使用 {} 包裹代码块,即使只有一行代码。

1.4 赋值与相等的混淆

if (x = 5) 是合法的 C 语言代码,但它表示"把 5 赋值给 x,然后判断 x 是否为真"。因为 5 非 0,所以条件永远成立。我们要的通常是 if (x == 5)

防御技巧:

使用 Yoda 表达式 ,将常量写在左边。如 if (5 == x)。如果你不小心写成 if (5 = x),编译器会直接报错,因为常量不能被赋值。

实战代码:成绩分段与合法性检查

c 复制代码
#include <stdio.h>

int main() {
    int score;
    printf("请输入成绩 (0-100): ");
    // 输入检查
    if (scanf("%d", &score) != 1) {
        printf("输入格式错误!\n");
        return 1;
    }

    // 逻辑判断
    if (score < 0 || score > 100) {
        printf("成绩必须在 0 到 100 之间\n");
    } else if (score >= 90) {
        printf("优秀 (A)\n");
    } else if (score >= 80) {
        printf("良好 (B)\n");
    } else if (score >= 60) {
        printf("及格 (C)\n");
    } else {
        printf("不及格 (F)\n");
    }
    return 0;
}

二、 switch/case:多分支选择

2.1 适用场景与限制

当需要对同一个变量进行多次等值判断时,switchif/else 更清晰且效率更高。

硬性限制:
switch 括号内的表达式结果必须是整型 (int, char, long等),不能是浮点数或字符串。case 后面必须是常量表达式。

2.2 break

核心概念:
switch 结构最著名的特性(也是最大的坑)是贯穿 。一旦匹配到一个 case,程序会从那个入口进入,并一直向下执行,不管后面的 case 条件是什么,直到遇到 break 或 switch 结束。

策略:
利用贯穿: 多个 case 执行相同逻辑。
防止贯穿: 每个 case 结束后务必加上 break;

2.3 default 的位置

default 处理所有未匹配的情况。它不一定非要放在最后,但为了可读性,通常放在末尾。

实战代码:菜单系统与月份天数(利用贯穿)

c 复制代码
#include <stdio.h>

int main() {
    int year, month, days;
    printf("请输入年份和月份: ");
    scanf("%d %d", &year, &month);

    switch (month) {
        case 2:
            // 闰年判断逻辑
            if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
                days = 29;
            else
                days = 28;
            break;
        case 4:
        case 6:
        case 9:
        case 11: 
            // 利用贯穿:这些月份都是30天
            days = 30;
            break;
        default:
            // 剩下的月份默认31天,包括非法月份也暂时归为此类
            days = 31;
            break;
    }
    printf("%d 年 %d 月有 %d 天\n", year, month, days);
    return 0;
}

三、 for 循环:计数与遍历

3.1 For 循环的三段式结构

语法: for (初始化; 条件判断; 迭代更新) { 循环体 }

执行顺序:

1.初始化 (只执行一次)。
2.条件判断 (若为假,循环结束)。
3.循环体
4.迭代更新 (如 i++)。

5.回到第 2 步。

3.2 循环变量的作用域

在 C99 标准之前,int i 必须在循环外定义。C99 开始支持 for (int i = 0; ...),此时变量 i 的作用域仅限于循环体内,循环结束后 i 立即销毁。这是一个良好的编程习惯,避免变量污染。

3.3 常见陷阱

分号陷阱:
for (int i=0; i<10; i++); { printf... }。注意那个多余的分号!这会导致循环空转 10 次,然后只打印一次。

边界错误:

遍历长度为 N 的数组,条件应为 i < N。如果写成 i <= N,就会发生越界访问。

死循环:
for(;;) 是标准的死循环写法,等同于 while(1)

实战代码:打印九九乘法表(嵌套循环)

c 复制代码
#include <stdio.h>

int main() {
    // 外层循环控制行
    for (int i = 1; i <= 9; i++) {
        // 内层循环控制列
        for (int j = 1; j <= i; j++) {
            // \t 是制表符,用于对齐
            printf("%d*%d=%-2d\t", j, i, i * j);
        }
        printf("\n"); // 每行结束换行
    }
    return 0;
}

四、 while / do-while:条件驱动循环

4.1 While 循环

特点: 先判断,后执行。如果一开始条件就不满足,循环体一次都不会执行。
适用: 迭代次数不确定,只知道停止条件的场景。

4.2 Do-While 循环

特点: 先执行,后判断。循环体至少执行一次。
适用: 无论如何都需要先做一次操作的场景,比如输入密码直到正确为止。
注意: do { ... } while (条件); 最后的分号不能少。

4.3 哨兵值与 EOF

在处理输入流时,我们常用 while 循环读取数据,直到遇到特定的哨兵值 (如 -1)或文件结束符 (EOF )。
写法: while (scanf("%d", &num) != EOF)while ((ch = getchar()) != '\n')

实战代码:输入验证(do-while)

c 复制代码
#include <stdio.h>

int main() {
    int number;
    do {
        printf("请输入一个正数: ");
        scanf("%d", &number);
        if (number <= 0) {
            printf("错误!必须是正数,请重试。\n");
        }
    } while (number <= 0); // 如果输入非正数,继续循环
    
    printf("你输入的正数是: %d\n", number);
    return 0;
}

五、 break/continue 与循环控制

5.1 break:跳出当前层

作用:

立即终止当前所在的循环(for, while, do-while)或 switch 语句,程序控制权跳到循环体外的下一条语句。
注意: break 只能跳出一层循环。在嵌套循环中,内层的 break 不会影响外层循环。

5.2 continue:跳过本次迭代

作用:

立即结束本次循环体的执行,直接进入下一次迭代(对于 for 循环,会先执行迭代更新部分,再检查条件)。
注意: continue 不能用于 switch 语句,除非该 switch 嵌套在循环中。

5.3 goto:被封印的禁术

goto 可以无条件跳转到函数内的任意标签。虽然它在处理深度嵌套循环的错误跳出时很方便,但滥用会导致代码逻辑像面条一样混乱。新手应尽量避免使用。

实战代码:查找元素与跳过特定值

c 复制代码
#include <stdio.h>

int main() {
    int sum = 0;
    for (int i = 1; i <= 10; i++) {
        if (i == 5) {
            printf("跳过数字 5\n");
            continue; // 不执行下面的累加,直接进入 i=6
        }
        if (sum > 20) {
            printf("总和超过 20,提前结束查找\n");
            break; // 彻底终止循环
        }
        sum += i;
        printf("加 %d, 当前总和: %d\n", i, sum);
    }
    return 0;
}

六、 练习题

题目 1: if (-1) 的条件判断结果是真还是假?

题目 2: 代码 int x=0; if(x=5) printf("A"); else printf("B"); 会输出什么?

题目 3: 逻辑表达式 (3 > 5) && (printf("Run")) 执行后,屏幕上会打印 "Run" 吗?

题目 4: switch 语句中,如果 case 后面没有 break,会发生什么现象?

题目 5: switch 括号中的表达式可以是 double 类型吗?

题目 6: case 后面跟的必须是什么类型的表达式?

题目 7: for(int i=0; i<5; i++); printf("%d", i); 这段代码输出结果是多少?(注意分号)

题目 8:while 循环中,初始化变量通常在哪里进行?

题目 9: do-while 循环和 while 循环最大的区别是什么?

题目 10: 想要在循环中直接跳过当次迭代的剩余代码,进入下一次循环,应该用什么关键字?

题目 11: 想要从多层嵌套循环的最内层直接跳出所有循环,除了 goto,通常还可以使用什么变量技巧?

题目 12: 代码 int i=0; while(i<3) { i++; continue; printf("%d", i); } 的输出是什么?

题目 13: break 语句能不能直接用在 if 语句块中(假设 if 不在循环或 switch 里)?

题目 14: 下列循环 for(int i=0; i!=10; i+=2) 是死循环吗?

题目 15: 下列循环 for(int i=0; i!=10; i+=3) 是死循环吗?

七、 解析

题 1 解析
答案: 真。
详解:

C 语言中,非 0 即真。-1 不是 0,所以被视为真。

题 2 解析
答案: 输出 "A"。
详解:

if(x=5) 是赋值表达式。x 被赋值为 5,表达式的值为 5(非0),故条件为真。这是经典的赋值混淆错误。

题 3 解析
答案: 不会。
详解:

逻辑与 && 具有短路特性。左边 3 > 5 为假 (0),整个表达式必为假,右边的 printf 不会被执行。

题 4 解析
答案: 贯穿 (Fallthrough)。
详解:

程序会继续执行下一个 case 的代码块,直到遇到 break 或 switch 结束。

题 5 解析
答案: 不可以。
详解:

switch 只能用于整型 (int, char, enum 等),不支持浮点数。

题 6 解析
答案: 常量表达式。
详解:

case 后的值必须在编译时就能确定,不能是变量(如 case n: 是非法的)。

题 7 解析
答案: 5。
详解:

注意 for 后面有一个分号 ;。这意味着循环体是空的,循环仅负责让 i 自增到 5。循环结束后执行 printf,此时 i 为 5。

题 8 解析
答案: 循环语句之前。
详解:

while 只有条件判断,变量初始化必须在进入 while 之前完成。

题 9 解析
答案: do-while 保证循环体至少执行一次。
详解:

do-while 是后测试循环,先做再判断;while 是前测试循环。

题 10 解析
答案: continue
详解:

continue 终止本次迭代,break 终止整个循环。

题 11 解析
答案: 标志位 (Flag) 变量。
详解:

设置一个变量(如 int found = 1;),在外层循环中检查这个变量,如果满足则 break

题 12 解析
答案: 什么都不输出。
详解:

每次 i++ 后立即执行 continue,跳过了后面的 printf

题 13 解析
答案: 不能。
详解:

break 专门用于循环语句和 switch 语句。单独的 if 语句中不能使用 break

题 14 解析
答案: 不是。
详解:

i 的序列是 0, 2, 4, 6, 8, 10。当 i 变为 10 时,i != 10 为假,循环正常结束。

题 15 解析
答案: 是死循环(可能导致整数溢出)。
详解:

i 的序列是 0, 3, 6, 9, 12... 永远不会等于 10,条件 i != 10 永远为真。这是一个经典的步长与边界不匹配错误。

日期:2025年2月10日

专栏:C语言

相关推荐
ZHOUPUYU2 小时前
PHP 8.3网关优化:我用JIT将QPS提升300%的真实踩坑录
开发语言·php
寻寻觅觅☆6 小时前
东华OJ-基础题-106-大整数相加(C++)
开发语言·c++·算法
YJlio6 小时前
1.7 通过 Sysinternals Live 在线运行工具:不下载也能用的“云端工具箱”
c语言·网络·python·数码相机·ios·django·iphone
l1t7 小时前
在wsl的python 3.14.3容器中使用databend包
开发语言·数据库·python·databend
赶路人儿7 小时前
Jsoniter(java版本)使用介绍
java·开发语言
ceclar1238 小时前
C++使用format
开发语言·c++·算法
码说AI8 小时前
python快速绘制走势图对比曲线
开发语言·python
Gofarlic_OMS8 小时前
科学计算领域MATLAB许可证管理工具对比推荐
运维·开发语言·算法·matlab·自动化
星空下的月光影子8 小时前
易语言开发从入门到精通:补充篇·网络爬虫与自动化采集分析系统深度实战·HTTP/HTTPS请求·HTML/JSON解析·反爬策略·电商价格监控·新闻资讯采集
开发语言
老约家的可汗8 小时前
初识C++
开发语言·c++