HiHuo
首页
博客
手册
工具
关于
首页
博客
手册
工具
关于
  • C 语言入门教程

    • C语言系列教程
    • 第01章 基础概念与编译
    • 第02章 数据类型与变量
    • 第03章 运算符
    • 第04章 输入输出
    • 第05章 流程控制
    • 第06章 数组
    • 第07章 指针
    • 第08章 函数
    • 第09章 构造类型

第05章 流程控制

5.1 if-else条件语句

5.1.1 if语句

语法:

if (条件表达式)
{
    // 条件为真时执行的语句
}

示例:

#include <stdio.h>

int main(void)
{
    int age;

    printf("请输入年龄: ");
    scanf("%d", &age);

    if (age >= 18)
    {
        printf("您已成年\n");
    }

    return 0;
}

5.1.2 if-else语句

语法:

if (条件表达式)
{
    // 条件为真时执行
}
else
{
    // 条件为假时执行
}

示例: 判断奇偶

#include <stdio.h>

int main(void)
{
    int num;

    printf("请输入整数: ");
    scanf("%d", &num);

    if (num % 2 == 0)
    {
        printf("%d 是偶数\n", num);
    }
    else
    {
        printf("%d 是奇数\n", num);
    }

    return 0;
}

5.1.3 if-else if-else多分支

语法:

if (条件1)
{
    // 语句1
}
else if (条件2)
{
    // 语句2
}
else if (条件3)
{
    // 语句3
}
else
{
    // 以上条件都不满足时执行
}

示例: 成绩等级判断

#include <stdio.h>

int main(void)
{
    int score;

    printf("请输入成绩: ");
    scanf("%d", &score);

    if (score < 0 || score > 100)
    {
        printf("成绩无效!\n");
    }
    else if (score >= 90)
    {
        printf("等级: A (优秀)\n");
    }
    else if (score >= 80)
    {
        printf("等级: B (良好)\n");
    }
    else if (score >= 70)
    {
        printf("等级: C (中等)\n");
    }
    else if (score >= 60)
    {
        printf("等级: D (及格)\n");
    }
    else
    {
        printf("等级: E (不及格)\n");
    }

    return 0;
}

5.1.4 嵌套if语句

示例: 判断闰年

#include <stdio.h>

int main(void)
{
    int year;

    printf("请输入年份: ");
    scanf("%d", &year);

    if (year % 4 == 0)
    {
        if (year % 100 == 0)
        {
            if (year % 400 == 0)
            {
                printf("%d 是闰年\n", year);
            }
            else
            {
                printf("%d 不是闰年\n", year);
            }
        }
        else
        {
            printf("%d 是闰年\n", year);
        }
    }
    else
    {
        printf("%d 不是闰年\n", year);
    }

    return 0;
}

优化版本(使用逻辑运算符):

#include <stdio.h>

int main(void)
{
    int year;

    printf("请输入年份: ");
    scanf("%d", &year);

    if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
    {
        printf("%d 是闰年\n", year);
    }
    else
    {
        printf("%d 不是闰年\n", year);
    }

    return 0;
}

5.1.5 悬空else问题

问题代码:

int a = 5, b = 10;

if (a > 0)
    if (b > 0)
        printf("a和b都是正数\n");
else  // 这个else属于哪个if?
    printf("a不是正数\n");

⚠️ 规则: else总是与最近的未匹配的if配对。

正确理解:

if (a > 0)
{
    if (b > 0)
    {
        printf("a和b都是正数\n");
    }
    else  // 属于内层if
    {
        printf("a是正数,b不是正数\n");
    }
}

建议: 总是使用花括号{},避免混淆。

5.2 switch-case分支语句

5.2.1 switch基本用法

语法:

switch (表达式)
{
    case 常量1:
        // 语句1
        break;
    case 常量2:
        // 语句2
        break;
    default:
        // 默认语句
        break;
}

示例: 菜单选择

#include <stdio.h>

int main(void)
{
    int choice;

    printf("========菜单========\n");
    printf("1. 新建文件\n");
    printf("2. 打开文件\n");
    printf("3. 保存文件\n");
    printf("4. 退出\n");
    printf("请选择: ");
    scanf("%d", &choice);

    switch (choice)
    {
        case 1:
            printf("新建文件\n");
            break;
        case 2:
            printf("打开文件\n");
            break;
        case 3:
            printf("保存文件\n");
            break;
        case 4:
            printf("退出程序\n");
            break;
        default:
            printf("无效选择!\n");
            break;
    }

    return 0;
}

5.2.2 case穿透

如果case后面没有break,会继续执行后面的case:

#include <stdio.h>

int main(void)
{
    int day;

    printf("输入星期几(1-7): ");
    scanf("%d", &day);

    switch (day)
    {
        case 1:
        case 2:
        case 3:
        case 4:
        case 5:
            printf("工作日\n");
            break;
        case 6:
        case 7:
            printf("周末\n");
            break;
        default:
            printf("无效输入\n");
            break;
    }

    return 0;
}

5.2.3 switch的限制

限制1: 表达式必须是整型或字符型

// 正确
switch (num)     // int
switch (ch)      // char

// 错误
switch (f)       // float不可以
switch (str)     // 字符串不可以

限制2: case后面必须是常量表达式

#define MAX 100
const int N = 10;

int a = 5;

switch (a)
{
    case 1:      // 正确:字面常量
        break;
    case MAX:    // 正确:宏常量
        break;
    case N:      // 错误:const变量不是常量表达式!
        break;
    case a + 1:  // 错误:变量表达式
        break;
}

5.2.4 switch vs if-else

switch适用场景:

  • 等值判断
  • 分支较多
  • 条件是离散的常量值

if-else适用场景:

  • 范围判断
  • 复杂的逻辑条件
  • 浮点数判断

示例对比:

// 使用switch(不推荐)
switch (score / 10)
{
    case 10:
    case 9:
        printf("A\n");
        break;
    case 8:
        printf("B\n");
        break;
    // ...
}

// 使用if-else(推荐)
if (score >= 90)
    printf("A\n");
else if (score >= 80)
    printf("B\n");
// ...

5.3 while循环

5.3.1 while基本用法

语法:

while (条件表达式)
{
    // 循环体
}

特点: 先判断条件,再执行循环体(可能一次都不执行)。

示例: 计算1到100的和

#include <stdio.h>

int main(void)
{
    int i = 1;
    int sum = 0;

    while (i <= 100)
    {
        sum += i;
        i++;
    }

    printf("1到100的和: %d\n", sum);  // 5050

    return 0;
}

5.3.2 while实现输入验证

#include <stdio.h>

int main(void)
{
    int num;
    int ret;

    printf("请输入1-10之间的整数: ");

    while (1)  // 无限循环
    {
        ret = scanf("%d", &num);

        // 清空缓冲区
        int c;
        while ((c = getchar()) != '\n' && c != EOF);

        if (ret != 1)
        {
            printf("输入格式错误!请重新输入: ");
            continue;
        }

        if (num >= 1 && num <= 10)
        {
            break;  // 输入正确,跳出循环
        }

        printf("数字超出范围!请重新输入: ");
    }

    printf("您输入的数字是: %d\n", num);

    return 0;
}

5.3.3 死循环

常见死循环形式:

// 形式1
while (1)
{
    // ...
}

// 形式2
while (true)
{
    // ...
}

// 形式3
for (;;)
{
    // ...
}

示例: 菜单程序

#include <stdio.h>

int main(void)
{
    int choice;

    while (1)
    {
        printf("\n========菜单========\n");
        printf("1. 选项1\n");
        printf("2. 选项2\n");
        printf("0. 退出\n");
        printf("请选择: ");
        scanf("%d", &choice);

        if (choice == 0)
        {
            printf("退出程序\n");
            break;
        }

        switch (choice)
        {
            case 1:
                printf("执行选项1\n");
                break;
            case 2:
                printf("执行选项2\n");
                break;
            default:
                printf("无效选择!\n");
        }
    }

    return 0;
}

5.4 do-while循环

5.4.1 do-while基本用法

语法:

do
{
    // 循环体
} while (条件表达式);

特点: 先执行循环体,再判断条件(至少执行一次)。

示例: 猜数字游戏

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
    int secret, guess;

    srand(time(NULL));
    secret = rand() % 100 + 1;  // 1-100随机数

    printf("猜数字游戏(1-100)\n");

    do
    {
        printf("请输入你的猜测: ");
        scanf("%d", &guess);

        if (guess > secret)
        {
            printf("太大了!\n");
        }
        else if (guess < secret)
        {
            printf("太小了!\n");
        }
        else
        {
            printf("恭喜你,猜对了!\n");
        }

    } while (guess != secret);

    return 0;
}

5.4.2 while vs do-while

特性whiledo-while
判断时机先判断再执行先执行再判断
最少执行次数0次1次
使用场景可能不需要执行至少需要执行一次

示例对比:

// while: 可能一次都不执行
int i = 10;
while (i < 10)
{
    printf("不会执行\n");
}

// do-while: 至少执行一次
int j = 10;
do
{
    printf("会执行一次\n");
} while (j < 10);

5.5 for循环

5.5.1 for基本用法

语法:

for (初始化; 条件判断; 更新)
{
    // 循环体
}

执行流程:

  1. 执行初始化(只执行一次)
  2. 判断条件
  3. 如果条件为真,执行循环体
  4. 执行更新
  5. 返回步骤2

示例: 打印1到10

#include <stdio.h>

int main(void)
{
    for (int i = 1; i <= 10; i++)
    {
        printf("%d ", i);
    }
    printf("\n");

    return 0;
}

5.5.2 for循环的灵活性

省略初始化:

int i = 1;
for (; i <= 10; i++)
{
    printf("%d ", i);
}

省略更新:

for (int i = 1; i <= 10; )
{
    printf("%d ", i);
    i++;
}

省略条件(死循环):

for (int i = 1; ; i++)
{
    if (i > 10) break;
    printf("%d ", i);
}

全部省略(死循环):

for (;;)
{
    // 等价于 while(1)
}

多个初始化和更新:

for (int i = 0, j = 10; i < j; i++, j--)
{
    printf("i=%d, j=%d\n", i, j);
}

5.5.3 常用for循环模式

正向遍历:

for (int i = 0; i < n; i++)
{
    // ...
}

反向遍历:

for (int i = n - 1; i >= 0; i--)
{
    // ...
}

步长为2:

for (int i = 0; i < n; i += 2)
{
    // ...
}

嵌套循环:

// 打印九九乘法表
for (int i = 1; i <= 9; i++)
{
    for (int j = 1; j <= i; j++)
    {
        printf("%d*%d=%2d ", j, i, i * j);
    }
    printf("\n");
}

输出:

1*1= 1
1*2= 2 2*2= 4
1*3= 3 2*3= 6 3*3= 9
...

5.5.4 for vs while

// for循环
for (int i = 0; i < 10; i++)
{
    printf("%d ", i);
}

// 等价的while循环
int i = 0;
while (i < 10)
{
    printf("%d ", i);
    i++;
}

选择建议:

  • 循环次数确定 → 使用for
  • 循环次数不确定 → 使用while

5.6 break语句

5.6.1 break的作用

  • 在循环中使用:跳出当前循环
  • 在switch中使用:跳出switch语句

示例: 查找第一个负数

#include <stdio.h>

int main(void)
{
    int arr[] = {1, 2, 3, -4, 5, 6};
    int n = sizeof(arr) / sizeof(arr[0]);

    for (int i = 0; i < n; i++)
    {
        if (arr[i] < 0)
        {
            printf("第一个负数在索引 %d\n", i);
            break;  // 找到后立即退出循环
        }
    }

    return 0;
}

5.6.2 break只跳出一层循环

#include <stdio.h>

int main(void)
{
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            if (j == 1)
            {
                break;  // 只跳出内层循环
            }
            printf("i=%d, j=%d\n", i, j);
        }
    }

    return 0;
}

输出:

i=0, j=0
i=1, j=0
i=2, j=0

5.6.3 跳出多层循环

方法1: 使用标志变量

#include <stdio.h>

int main(void)
{
    int found = 0;

    for (int i = 0; i < 5 && !found; i++)
    {
        for (int j = 0; j < 5; j++)
        {
            if (i * j == 6)
            {
                printf("找到: i=%d, j=%d\n", i, j);
                found = 1;
                break;
            }
        }
    }

    return 0;
}

方法2: 使用goto(不推荐,但有时很实用)

#include <stdio.h>

int main(void)
{
    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 5; j++)
        {
            if (i * j == 6)
            {
                printf("找到: i=%d, j=%d\n", i, j);
                goto end;  // 跳到标签位置
            }
        }
    }

end:
    printf("结束\n");

    return 0;
}

5.7 continue语句

5.7.1 continue的作用

跳过本次循环剩余部分,开始下一次循环。

示例: 打印奇数

#include <stdio.h>

int main(void)
{
    for (int i = 1; i <= 10; i++)
    {
        if (i % 2 == 0)
        {
            continue;  // 跳过偶数
        }
        printf("%d ", i);
    }
    printf("\n");

    return 0;
}

输出: 1 3 5 7 9

5.7.2 break vs continue

语句作用循环是否继续
break终止循环否
continue跳过本次循环是

示例对比:

// 使用break
for (int i = 0; i < 5; i++)
{
    if (i == 2) break;
    printf("%d ", i);  // 输出: 0 1
}

// 使用continue
for (int i = 0; i < 5; i++)
{
    if (i == 2) continue;
    printf("%d ", i);  // 输出: 0 1 3 4
}

5.8 goto语句

5.8.1 goto基本用法

语法:

goto 标签;

标签:
    // 语句

示例:

#include <stdio.h>

int main(void)
{
    int i = 0;

loop:
    printf("%d ", i);
    i++;
    if (i < 5)
    {
        goto loop;
    }

    printf("\n");

    return 0;
}

5.8.2 goto的合理使用

场景1: 错误处理

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    FILE *fp1 = NULL;
    FILE *fp2 = NULL;
    char *buf = NULL;

    fp1 = fopen("file1.txt", "r");
    if (fp1 == NULL)
    {
        goto cleanup;
    }

    fp2 = fopen("file2.txt", "w");
    if (fp2 == NULL)
    {
        goto cleanup;
    }

    buf = malloc(1024);
    if (buf == NULL)
    {
        goto cleanup;
    }

    // 正常处理...

cleanup:
    if (buf) free(buf);
    if (fp2) fclose(fp2);
    if (fp1) fclose(fp1);

    return 0;
}

场景2: 跳出多层循环

for (int i = 0; i < 100; i++)
{
    for (int j = 0; j < 100; j++)
    {
        if (condition)
        {
            goto end;
        }
    }
}
end:
    // ...

⚠️ 注意: 尽量少用goto,优先使用结构化控制语句。

5.9 实践练习

练习1: 素数判断

编写程序判断一个数是否为素数:

#include <stdio.h>
#include <stdbool.h>

bool isPrime(int n)
{
    if (n < 2) return false;
    if (n == 2) return true;
    if (n % 2 == 0) return false;

    for (int i = 3; i * i <= n; i += 2)
    {
        if (n % i == 0)
        {
            return false;
        }
    }

    return true;
}

int main(void)
{
    int num;

    printf("请输入一个正整数: ");
    scanf("%d", &num);

    if (isPrime(num))
    {
        printf("%d 是素数\n", num);
    }
    else
    {
        printf("%d 不是素数\n", num);
    }

    return 0;
}

练习2: 打印图案

打印空心正方形:

#include <stdio.h>

int main(void)
{
    int n;

    printf("请输入正方形边长: ");
    scanf("%d", &n);

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            if (i == 0 || i == n - 1 || j == 0 || j == n - 1)
            {
                printf("* ");
            }
            else
            {
                printf("  ");
            }
        }
        printf("\n");
    }

    return 0;
}

输入5时输出:

* * * * *
*       *
*       *
*       *
* * * * *

练习3: 最大公约数

使用辗转相除法求最大公约数:

#include <stdio.h>

int gcd(int a, int b)
{
    while (b != 0)
    {
        int temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}

int main(void)
{
    int a, b;

    printf("请输入两个正整数: ");
    scanf("%d%d", &a, &b);

    printf("%d 和 %d 的最大公约数是: %d\n", a, b, gcd(a, b));

    return 0;
}

5.10 常见错误

错误1: if后面多了分号

if (a > 0);  // 错误!分号表示空语句
{
    printf("a是正数\n");  // 这个块总是执行
}

// 正确写法
if (a > 0)
{
    printf("a是正数\n");
}

错误2: switch中忘记break

switch (choice)
{
    case 1:
        printf("选项1\n");
        // 忘记break,会继续执行case 2
    case 2:
        printf("选项2\n");
        break;
}

错误3: for循环变量作用域

for (int i = 0; i < 10; i++)
{
    // ...
}
printf("%d\n", i);  // 错误!i在for循环外不可见(C99)

错误4: 无限循环

// 错误:循环变量未更新
int i = 0;
while (i < 10)
{
    printf("%d ", i);
    // 忘记 i++
}

// 错误:判断条件永远为真
for (int i = 0; i >= 0; i++)  // i永远>=0
{
    // ...
}

错误5: continue用在switch中

switch (choice)
{
    case 1:
        if (error)
            continue;  // 错误!switch不是循环
        break;
}

// 正确:switch中应该用break
switch (choice)
{
    case 1:
        if (error)
            break;
        // ...
        break;
}

5.11 小结

本章学习了:

if-else条件语句及嵌套 switch-case多分支语句 while循环(先判断后执行) do-while循环(先执行后判断) for循环及其灵活用法 break跳出循环 continue跳过本次循环 goto语句的合理使用

重点提示:

  1. 总是使用花括号{},提高代码可读性
  2. switch的case后不要忘记break
  3. 注意死循环的条件
  4. break只跳出一层循环
  5. continue跳过本次循环,但循环继续
  6. goto谨慎使用,主要用于错误处理和跳出多层循环

上一章: 第04章 输入输出下一章: 第06章 数组

Prev
第04章 输入输出
Next
第06章 数组