22 程序控制语句详解:跳转控制(break、continue、goto)、死循环应用、程序控制编程实战

1 break 语句

1.1 介绍

        break 语句用于立即终止当前所在的循环或 switch 语句块的执行,将程序控制权转移到该语句块之后的下一条语句

  • 循环语句:提前退出循环体(仅退出最近的封闭循环)。
  • switch 语句:防止代码 "穿透" 执行下一个 case 分支。

1.2 功能流程图

1.3 在循环中使用 break

        break 语句在循环中用于在满足特定条件时提前终止循环,跳过循环中剩余的语句和后续的循环迭代。 

#include <stdio.h>

int main()
{
    for (int i = 0; i < 10; i++)
    {
        if (i == 3)
        {
            break; // 当 i=3 时终止循环
        }
        printf("%d \t", i); // 输出: 0 1 2
    }
    return 0;
}

        程序在 VS Code 中的运行结果如下所示:

1.4 嵌套循环中的 break

        在嵌套循环中,break 只会终止最近的封闭循环(内层循环),外层循环不受影响

#include <stdio.h>

int main()
{
    for (int i = 1; i <= 3; i++)
    {
        printf("外层循环:i = %d\n", i);

        for (int j = 1; j <= 5; j++)
        {
            if (j == 4)
            {
                break; // 只退出内层循环
            }
            printf("  内层循环:j = %d\n", j);
        }
    }
    return 0;
}

        程序在 VS Code 中的运行结果如下所示:

1.5 案例:质数判断

        编写一个程序,要求用户输入一个数字,判断该数字是否是质数(又称素数,是指在大于 1 的自然数中,除了 1 和它本身以外,不能被其他自然数整除的数)

方法一:使用布尔类型(C99 及以上)

#include <stdio.h>
#include <stdbool.h> // 引入布尔类型支持

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

    // 初始化质数标志
    // 质数是大于 1 的自然数,且只能被 1 和它本身整除
    // 这里使用三元运算符来初始化 isPrime
    // 如果 num 小于等于 1,则不是质数,否则是质数
    bool isPrime = num > 1 ? true : false;

    // 如果 num 大于 1,则进行质数判断
    // 从 2 开始判断到 num 的一半
    // 因为一个数的因数不可能大于它的一半
    // 例如:6 的因数是 1, 2, 3, 6,最大因数是 3
    for (int i = 2; i <= num / 2; i++)
    {
        if (num % i == 0)
        {
            isPrime = false; // 发现因数,不是质数
        }
    }

    // 输出结果
    if (isPrime) // 如果 isPrime 为 true,则 num 是质数
    {
        printf("%d 是质数。\n", num);
    }
    else // 如果 isPrime 为 false,则 num 不是质数
    {
        printf("%d 不是质数。\n", num);
    }

    return 0;
}

方法二:使用整型标志(兼容所有 C 版本)

#include <stdio.h>

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

    // 初始化质数标志(1 表示 true,0 表示 false)
    // 质数是大于 1 的自然数,且只能被 1 和它本身整除
    // 这里使用三元运算符来初始化 isPrime
    // 如果 num 小于等于 1,则不是质数,否则是质数
    int isPrime = num > 1 ? 1 : 0;

    // 如果 num 大于 1,则进行质数判断
    // 从 2 开始判断到 num 的一半
    // 因为一个数的因数不可能大于它的一半
    // 例如:6 的因数是 1, 2, 3, 6,最大因数是 3
    for (int i = 2; i <= num / 2; i++)
    {
        if (num % i == 0)
        {
            isPrime = 0; // 发现因数,不是质数
        }
    }

    // 输出结果
    if (isPrime)
    {
        printf("%d 是质数。\n", num);
    }
    else
    {
        printf("%d 不是质数。\n", num);
    }

    return 0;
}

程序优化:

  1. 将循环上限从 num/2 优化为 sqrt(num)(通过 i*i <= num 实现)。
  2. 在循环条件中加入 && isPrime,使得一旦确定非质数就立即终止循环。
  3. 使用三元运算符简化输出语句,添加更友好的用户提示。
#include <stdio.h>

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

    // 初始化质数标志(1 表示 true,0 表示 false)
    // 质数是大于 1 的自然数,且只能被 1 和它本身整除
    // 这里使用三元运算符来初始化 isPrime
    // 如果 num 小于等于 1,则不是质数,否则是质数
    int isPrime = num > 1 ? 1 : 0;

    // 如果 num 大于 1,则进行质数判断
    // 将循环上限从 num / 2 优化为 sqrt(num) (通过 i *i <= num 实现)
    // 因为一个数的因数不可能大于它的平方根(可能比一半要小,优化循环次数)
    // 在循环条件中加入 && isPrime,使得一旦确定非质数就立即终止循环
    for (int i = 2; i * i <= num && isPrime; i++)
    {
        if (num % i == 0)
        {
            isPrime = 0; // 发现因数,标记为非质数
        }
    }

    // 输出结果
    // %s 用于格式化字符串,在 printf 中使用可以输出字符串
    printf("%d %s质数!\n", num, isPrime ? "是" : "不是");

    return 0;
}

        程序在 VS Code 中的多次运行结果如下所示:

提示:

  • 质数判断只需检查到平方根即可,这是数学上的优化。
  • %s 用于格式化字符串,在 printf 中使用可以输出字符串。 

1.6 注意事项

  1. 使用范围限制:break 只能用于 switch 或循环语句中,在其它地方使用会导致编译错误
  2. 嵌套循环处理:break 只能退出当前所在循环,多层退出需使用多个 break 或标志变量
  3. switch 语句中的穿透效应:在 switch 中可以使用 break 防止执行不需要的 case 分支。
  4. 替代方案:对于复杂嵌套循环,可考虑使用 goto 语句(虽不推荐,但在某些场景下是合理选择)。

1.7 最佳实践

  • 在简单循环中优先使用 break 提前退出。
  • 对于嵌套循环,考虑重构代码或使用标志变量控制外层循环。
  • 在 switch 语句中建议始终为每个 case 添加明确的 break(除非有意利用穿透特性)。

2 continue 语句

2.1 介绍

        continue 是循环控制语句,只能用于 for、while 或 do-while 循环中

        它的作用是跳过当前循环迭代的剩余部分,直接进入下一次循环

    2.2 功能流程图

    2.3 在循环中使用 continue

            continue 语句用于控制循环流程,当满足特定条件时跳过当前迭代的剩余部分,直接进入下一次循环迭代

    • 跳过当前迭代:当 continue 被执行时,循环体中 continue 之后的代码将被忽略。
    • 进入下一次迭代:立即开始下一次循环检查(对于 for 循环,会先执行迭代部分)。
    #include <stdio.h>
    
    int main()
    {
        // 示例:打印 1-10,跳过数字 5
        for (int i = 1; i <= 10; i++)
        {
            if (i == 5)
            {
                continue; // 跳过数字 5
            }
            printf("%d ", i);
        }
    
        return 0;
    }

            程序在 VS Code 中的运行结果如下所示:

    2.4 嵌套循环中的 continue

            在嵌套循环中,continue 只影响最内层的循环。如果需要影响外层循环,需要使用标记变量或 goto(不推荐)。

    #include <stdio.h>
    
    int main()
    {
        for (int i = 1; i <= 3; i++)
        {
            printf("外层循环:i = %d\n", i);
    
            for (int j = 1; j <= 4; j++)
            {
                if (j == 3)
                {
                    continue; // 只跳过内层循环的当前迭代
                }
                printf("  内层循环:j = %d\n", j);
            }
        }
        return 0;
    }

            程序在 VS Code 中的运行结果如下所示:

    2.5 案例:逢七过游戏

            实现一个 "逢七过" 游戏,输出 1 到 100(包含 1 和 100)的数字,但需要跳过以下数字:

    1. 7 的倍数
    2. 包含数字 7 的数字(个位或十位为 7)
    #include <stdio.h>
    
    int main()
    {
        int counter = 0; // 计数器,用于记录输出的数字个数
    
        // 循环从 1 到 100
        for (int i = 1; i <= 100; i++)
        {
            // 检查当前数字是否是 7 的倍数,个位数是否为 7,或者十位数是否为 7
            if (i % 7 == 0 || i % 10 == 7 || i / 10 == 7)
            {
                continue; // 如果条件满足,跳过当前数字
            }
    
            printf("%d ", i); // 输出符合条件的数字
            counter++;        // 增加计数器
    
            // 每 10 个数字换行
            if (counter % 10 == 0)
            {
                printf("\n");
            }
        }
    
        return 0;
    }
    

            程序在 VS Code 中的运行结果如下所示:

    2.6 注意事项

    1. 与 break 的区别:

    • continue:跳过当前迭代,继续下一次循环
    • break:完全终止循环

    2. 循环变量更新:

    • 在 while 循环中,确保 continue 不会跳过循环变量的更新
    • 错误的 continue 放置可能导致无限循环。
    • 错误示例:
    #include <stdio.h>
    
    int main()
    {
        // 错误:可能导致无限循环
        int i = 0;
        while (i < 5)
        {
            if (i % 2 == 0)
            {
                continue; // 跳过 i 的递增
            }
            printf("%d", i);
            i++;
        }
    
        return 0;
    }
    • 正确写法:
    #include <stdio.h>
    
    int main()
    {
        int i = 0;
        while (i < 5)
        {
            if (i % 2 == 0)
            {
                printf("跳过偶数: %d\n", i);
                i++; // 这里需要先自增,否则会陷入死循环
                continue;
            }
            printf("奇数: %d\n", i);
            i++; // 自增
        }
    
        return 0;
    }

    2.7 最佳实践

    1. 明确使用场景:

    1. 只在确实需要跳过当前迭代时使用 continue。
    2. 避免滥用,特别是简单的 if-else 结构可以替代时。

    2. 代码可读性:

    1. 复杂条件拆分为多个 if 语句:
    // 不推荐
    if (condition1 && condition2 || condition3) {
        continue;
    }
    
    // 推荐
    if (condition1) {
        if (condition2 || condition3) {
            continue;
        }
    }

    3. 循环变量安全:

    1. 在 while 循环中,确保 continue 不会跳过循环变量的更新
    2. 考虑将循环变量的更新放在循环开始处。

    4. 注释说明:

    1. 对复杂的 continue 使用添加注释,说明跳过的原因。

    5. 替代方案:

    1. 对于简单的条件判断,考虑使用 if-else 结构替代 continue:
    // 使用 continue
    for (...) {
        if (condition) continue;
        // 执行代码
    }
    
    // 替代方案
    for (...) {
        if (!condition) {
            // 执行代码
        }
    }

    3 goto 语句

    3.1 介绍

            goto 语句是一个无条件跳转语句它允许程序跳转到同一函数或同一作用域块内的指定标签(label)处继续执行。标签名称需符合标识符规范。

      3.2 语法格式

      goto label_name;  // 跳转到指定标签
      // ...
      label_name:       // 定义标签
          statement;    // 跳转到这里执行
      • 注意:goto 后面引用了未定义的标签会导致编译错误。 

      3.3 功能流程图

      3.4 案例:跳过代码块

      #include <stdio.h>
      
      int main()
      {
          printf("Start \n"); // 开始
      
          // 跳转到标签 label1 处执行
          goto label1; // 跳过下面的两条打印语句
      
          // 以下语句由于 goto 的存在不会被执行
          printf("ok1 \n"); // 跳过
          printf("ok2 \n"); // 跳过
      
      label1:
          // 跳转到这里执行
          printf("ok3 \n"); // 执行
          printf("ok4 \n"); // 执行
          printf("End \n"); // 结束
      
          return 0;
      }

              程序在 VS Code 中的运行结果如下所示:

      3.5 案例:条件跳过循环

      #include <stdio.h>
      
      int main()
      {
          int i = 11;
      
          // 使用 goto 跳转到循环结束标签
          if (i > 10)
          {
              goto end_loop; // 直接跳转到循环结束标签
          }
      
          // 以下循环不会执行
          for (i = 0; i < 5; i++)
          {
              printf("i = %d\n", i); // 这行不会执行
          }
      
      end_loop:
          printf("跳过上面的循环-未执行循环\n");
      
          return 0;
      }

              程序在 VS Code 中的运行结果如下所示:

      3.6 使用 goto 实现循环

              虽然不推荐,但 goto 可以用于实现循环逻辑:

      #include <stdio.h>
      
      int main()
      {
          int i = 1; // 初始化计数器
      
      start: // 定义循环起始标签
          if (i <= 5) // 循环条件
          {
              printf("%d ", i); // 打印当前值
              i++;              // 递增计数器
              goto start;       // 跳回循环起始处
          }
      
          return 0; // 循环结束
      }

              程序在 VS Code 中的运行结果如下所示:

      3.7 注意事项

      1. 关键限制:

      • 不能跨函数跳转(不能从一个函数跳转到另一个函数的标签)
      • 不能跨多层嵌套作用域跳转
      • 只能跳转到同一函数内的标签

      2. 使用建议:

      • 通常被认为是不良编程实践,可能导致代码难以理解和维护。
      • 不建议在常规开发中使用。
      • 需要理解其执行流程以阅读包含 goto 的代码。

      3.8 最佳实践

      1. 避免使用:在大多数情况下,使用结构化控制流(for、while、do-while)更可取。
      2. 有限使用场景:
        • 从深层嵌套中跳出(但通常有更好的结构化解决方案)。
        • 某些特定算法实现(如状态机)。
      3. 代码可读性:
        • 如果必须使用,添加详细注释说明原因。
        • 保持标签命名清晰且有意义。
      4. 替代方案:
        • 使用 break 和 continue 控制循环。
        • 使用函数封装复杂逻辑。
        • 使用标志变量控制流程。

              goto 语句虽然功能强大,但会破坏程序的结构化特性,应谨慎使用。在绝大多数情况下,结构化编程技术可以提供更清晰、更易维护的解决方案。


      4 死循环

      4.1 介绍

              死循环(Infinite Loop)是指一种无法自行终止的循环结构,程序会一直重复执行循环体内的代码,除非被外部干预(如用户中断、系统信号或程序异常)。具有以下特点:

      • 循环条件始终为真。
      • 不会自然退出。
      • 通常需要明确的退出机制(如 break 语句)。

      4.2 死循环的实现方式

      使用 while 循环

      while (1) {
          // 循环体代码
          // 需要 break 语句或其他方式退出
      }

      使用 do-while 循环

      do {
          // 循环体代码
          // 需要 break 语句或其他方式退出
      } while (1);

      使用 for 循环

      for (;;) {
          // 循环体代码
          // 需要 break 语句或其他方式退出
      }

      使用 goto 语句

      start:
          // 循环体代码
          // 需要 break 语句或其他方式退出
          goto start;

      4.3 退出死循环的常见方式

      break 语句

      while (1) {
          if (exit_condition) {
              break;  // 退出循环
          }
          // 其他代码
      }

      return 语句

      void infinite_loop() {
          while (1) {
              if (exit_condition) {
                  return;  // 退出函数
              }
              // 其他代码
          }
      }

      goto 语句

      while (1) {
          if (exit_condition) {
              goto exit_loop;  // 跳转到循环外
          }
          // 其他代码
      }
      exit_loop:

      4.4 案例:命令行菜单系统

      #include <stdio.h>
      #include <stdlib.h>
      
      int main() {
          int choice;
          
          while (1) {
              printf("\n菜单:\n");
              printf("1. 选项1\n");
              printf("2. 选项2\n");
              printf("3. 退出\n");
              printf("请选择: ");
              scanf("%d", &choice);
              
              switch (choice) {
                  case 1:
                      printf("执行选项1\n");
                      break;
                  case 2:
                      printf("执行选项2\n");
                      break;
                  case 3:
                      printf("退出程序\n");
                      return 0;  // 退出程序
                  default:
                      printf("无效选择\n");
              }
          }
      }

      注意: 

      1. 必须有退出机制
        • 死循环必须提供明确的退出条件(如用户输入、信号触发或逻辑判断),否则程序将无法终止
        • 示例:while (1) { if (user_input == 'q') break; }
      2. 防止逻辑错误
        • 确保循环条件或退出逻辑正确,避免因条件永远为真导致 “真死循环”
        • 错误示例:while (i < 10) { if (i == 5) continue; i++; }(i 停在5)

      5 编程练习

      5.1 水仙花数判断

              编写一个 C 语言程序,该程序直接在 main 函数中实现判断一个整数是否为水仙花数的功能。水仙花数是一个三位数,其各位数字的立方和等于该数本身。例如,153 是一个水仙花数,因为 1^3 + 5^3 + 3^3 = 153。

      #include <stdio.h>
      
      int main()
      {
          int num;      // 存储用户输入的整数
          int ones;     // 存储个位数字
          int tens;     // 存储十位数字
          int hundreds; // 存储百位数字
          int cubeSum;  // 存储各位数字的立方和
      
          // 提示用户输入一个三位数
          printf("请输入一个整数(三位数):");
          scanf("%d", &num);
      
          // 检查输入的数字是否为三位数(范围:100 到 999)
          if (num < 100 || num > 999)
          {
              printf("输入的不是一个三位数。\n");
              return 1; // 返回非零值表示程序异常退出
          }
      
          // 分解数字为个位、十位和百位
          ones = num % 10;        // 获取个位数:num 除以 10 的余数
          tens = (num / 10) % 10; // 获取十位数:num 除以 10 去掉个位后,再除以 10 的余数
          hundreds = num / 100;   // 获取百位数:num 除以 100 的整数部分
      
          // 计算各位数字的立方和
          cubeSum = ones * ones * ones +            // 个位数的立方
                    tens * tens * tens +            // 十位数的立方
                    hundreds * hundreds * hundreds; // 百位数的立方
      
          // 判断是否为水仙花数:立方和是否等于原始数字
          if (cubeSum == num)
          {
              printf("%d 是水仙花数。\n", num); // 输出是水仙花数
          }
          else
          {
              printf("%d 不是水仙花数。\n", num); // 输出不是水仙花数
          }
      
          return 0; // 程序正常退出
      }

              程序在 VS Code 中的多次运行结果如下所示:

      5.2 计算指定年份和月份的天数

              编写一个 C 语言程序,接收用户输入的月份(1-12)和年份,计算并输出该月份在指定年份中的天数。程序需正确处理闰年情况(闰年 2 月 29 天,平年 2 月 28 天)。 

      #include <stdio.h>
      
      int main()
      {
          int month, year; // 存储用户输入的月份和年份
          printf("请输入月份(1-12):");
          scanf("%d", &month);
          printf("请输入年份:");
          scanf("%d", &year);
      
          // 检查月份是否合法(1-12)
          if (month < 1 || month > 12)
          {
              printf("输入的月份不合法。\n");
              return 1; // 非零返回值表示异常结束
          }
      
          // 初始化天数,默认为 31 天(1、3、5、7、8、10、12 月)
          int days = 31;
      
          // 根据月份调整天数
          switch (month)
          {
          case 2: // 2 月:判断是否为闰年
              if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
              {
                  days = 29; // 闰年 2 月 29 天
              }
              else
              {
                  days = 28; // 平年 2 月 28 天
              }
              break;
          case 4:
          case 6:
          case 9:
          case 11:
              days = 30; // 4、6、9、11 月为 30 天
              break;
              // 其他月份(1、3、5、7、8、10、12 月)保持默认的 31 天
          }
      
          // 输出结果
          printf("%d年%d月有%d天。\n", year, month, days);
      
          return 0; // 程序正常结束
      }

              程序在 VS Code 中的多次运行结果如下所示:

      5.3 打印 1000 年到 9999 年之间的所有闰年 

              编写一个 C 语言程序,分别使用 for 循环、while 循环和 do-while 循环遍历 1000 年到 9999 年的所有年份,判断并打印闰年。闰年规则:能被 4 整除但不能被 100 整除,或能被 400 整除。将三种循环的实现合并到一个程序中,并优化输出格式(每 10 个年份换行)。

      #include <stdio.h>
      
      int main()
      {
          printf("使用 for 循环打印闰年:\n");
          int count = 0; // 计数器,每打印 10 个年份换行
          // 使用 for 循环遍历 1000 年到 9999 年
          for (int year = 1000; year <= 9999; year++)
          {
              // 判断是否为闰年
              if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
              {
                  printf("%d\t", year); // 打印闰年
                  count++;
                  // 每打印 10 个年份换行
                  if (count % 10 == 0)
                  {
                      printf("\n");
                  }
              }
          }
      
          printf("\n\n使用 while 循环打印闰年:\n");
          count = 0;       // 重置计数器
          int year = 1000; // 初始化年份为 1000
          // 使用 while 循环遍历 1000 年到 9999 年
          while (year <= 9999)
          {
              // 判断是否为闰年
              if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
              {
                  printf("%d\t", year); // 打印闰年
                  count++;
                  // 每打印 10 个年份换行
                  if (count % 10 == 0)
                  {
                      printf("\n");
                  }
              }
              year++; // 年份递增
          }
      
          printf("\n\n使用 do-while 循环打印闰年:\n");
          count = 0;   // 重置计数器
          year = 1000; // 重新初始化年份为 1000
          // 使用 do-while 循环遍历 1000 年到 9999 年
          do
          {
              // 判断是否为闰年
              if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
              {
                  printf("%d\t", year); // 打印闰年
                  count++;
                  // 每打印 10 个年份换行
                  if (count % 10 == 0)
                  {
                      printf("\n");
                  }
              }
              year++; // 年份递增
          } while (year <= 9999); // 循环条件
      
          return 0;
      }

      5.4 正序和逆序打印英文字母

              编写一个 C 语言程序,首先按顺序打印所有小写英文字母(a 到 z),然后以逆序打印所有大写英文字母(Z 到 A)。

      #include <stdio.h>
      
      int main()
      {
          // 打印小写字母 a 到 z
          printf("小写字母 a-z:\n");
          for (char c = 'a'; c <= 'z'; c++)
          {
              printf("%c ", c); // 打印当前字母并加空格
          }
          printf("\n"); // 换行
      
          // 打印大写字母 Z 到 A
          printf("大写字母 Z-A:\n");
          for (char c = 'Z'; c >= 'A'; c--)
          {
              printf("%c ", c); // 打印当前字母并加空格
          }
          printf("\n"); // 换行
      
          return 0;
      }

              程序在 VS Code 中的运行结果如下所示:

      5.5 计算交错序列的和

              编写一个 C 语言程序,计算并输出序列 1 - 1/2 + 1/3 - 1/4 + ... + 1/n 的和,其中 n 是一个用户指定的正整数(例如,n = 100)。序列中的每一项的符号交替出现,即第一项为正,第二项为负,第三项为正,以此类推。

      #include <stdio.h>
      
      int main()
      {
          int n;            // 用户指定的正整数 n
          double sum = 0.0; // 用来存储序列的和
          int sign = 1;     // 用来表示当前的符号,初始化为正(1)
      
          // 提示用户输入正整数 n
          printf("请输入一个正整数 n:");
          scanf("%d", &n); // 读取用户输入的 n
      
          // 计算交错序列的和
          for (int i = 1; i <= n; i++)
          {
              // 将当前项加到总和中,注意将 1 转换为 1.0 以避免整数除法
              sum += sign * 1.0 / i;
      
              // 改变符号,为下一次迭代做准备
              sign = -sign;
          }
      
          // 输出结果
          printf("序列 1 - 1/2 + 1/3 - ... + 1/%d 的和为: %f\n", n, sum);
      
          return 0;
      }

              程序在 VS Code 中的运行结果如下所示:

      5.6 回文数判断

              编写一个 C 语言程序,输入一个整型数,判断其是否为回文数(Palindrome Number)。回文数是指一个数字无论从左向右读还是从右向左读都一样的数。如果是回文数,输出 yes,否则输出 no。

      #include <stdio.h>
      
      int main()
      {
          int num, reversed = 0, original; // 存储用户输入的数字、反转后的数字和原始数字
      
          // 提示用户输入一个整数
          printf("请输入一个整数:");
          scanf("%d", &num);
      
          original = num; // 保存原始数字,用于后续比较
      
          // 反转数字
          while (num != 0)
          {
              reversed = reversed * 10 + num % 10; // 将当前数字的个位添加到反转数字中
              // reversed * 10 是将反转数字向左移动一位
              // num % 10 是获取当前数字的个位
              num /= 10; // 去除当前数字的个位
          }
      
          // 判断原始数字是否与反转后的数字相等
          if (original == reversed)
          {
              printf("yes\n"); // 是回文数
          }
          else
          {
              printf("no\n"); // 不是回文数
          }
      
          return 0;
      }

              程序在 VS Code 中的多次运行结果如下所示:

      5.7 计算 n 的阶乘

              编写一个 C 语言程序,利用 for 循环计算用户输入的整数 n 的阶乘(n!),并输出结果。

      #include <stdio.h>
      
      int main()
      {
          int number, factorial = 1; // 存储用户输入的整数和阶乘结果
      
          // 提示用户输入一个整数
          printf("请输入一个整数:");
          // 读取用户输入的数
          scanf("%d", &number);
      
          // 使用 for 循环计算阶乘
          // 由于 factorial 初始值为 1,所以循环变量可以从 2 开始
          for (int i = 2; i <= number; i++)
          {
              factorial *= i; // 累乘计算阶乘
          }
      
          // 输出结果
          printf("%d 的阶乘 %d!:%d\n", number, number, factorial);
      
          return 0;
      }

              程序在 VS Code 中的多次运行结果如下所示:

      5.8 货币换零钱组合问题

              某人想将一张面值 100 元的货币换成 10 元、5 元、2 元和 1 元面值的票子,要求换正好 40 张,并且每种票子至少各有一张。编写一个 C 语言程序,计算并输出满足条件的换法总数。

      题目分析:

              为了更高效地解决这个问题,我们需要考虑每种面额纸币的数量范围,并据此设置循环的边界条件。具体地,我们可以这样设定:

      • 10 元纸币:由于至少要有 1 张,且最多不能超过 9 张(否则总金额会超过 100 元),所以循环范围从 1 到 9。
      • 5 元纸币:在剩余金额和剩余张数(40 减去已选的10元纸币数量)的限制下,最多可以选取(100 - 10*已选10元数 - 2 - 1) / 5 张,但至少要有 1 张,所以循环范围需要根据已选的 10 元纸币数量动态计算。
      • 2 元纸币:同样地,其数量也需要在剩余金额和剩余张数的限制下确定,但至少要有 1 张。
      • 1 元纸币:最后,剩余的张数和金额将全部用于 1 元纸币,但也需要至少 1 张。
      #include <stdio.h>
      
      int main()
      {
          int count = 0; // 计数器,记录符合条件的组合数
      
          // 遍历 10 元纸币的可能数量(1 到 9张)
          for (int ten = 1; ten <= 9; ten++)
          {
              // 遍历 5 元纸币的可能数量(至少 1 张,最多不超过剩余金额和张数限制)
              // (100 - 10 * ten - 2 - 1) / 5 表示最多可以使用的 5 元纸币数量
              // 这里减去 2 是因为至少需要留出 1 张 2 元纸币和 1 张 1 元纸币
              for (int five = 1; five <= (100 - 10 * ten - 2 - 1) / 5; five++)
              {
                  // 遍历 2 元纸币的可能数量(至少 1 张,最多不超过剩余金额和张数限制)
                  // (100 - 10 * ten - 5 * five - 1) / 2 表示最多可以使用的 2 元纸币数量
                  // 这里减去 1 是因为至少需要留出 1 张 1 元纸币
                  for (int two = 1; two <= (100 - 10 * ten - 5 * five - 1) / 2; two++)
                  {
                      // 计算 1 元纸币的数量
                      int one = 40 - ten - five - two;
      
                      // 检查 1 元纸币数量是否合法(至少 1 张,且金额正确)
                      if (one >= 1 && (10 * ten + 5 * five + 2 * two + one) == 100)
                      {
                          count++;
                          printf("组合 %d: 10元x%d, 5元x%d, 2元x%d, 1元x%d\n",
                                 count, ten, five, two, one);
                      }
                  }
              }
          }
      
          printf("总共有 %d 种符合条件的换法\n", count); // 34
          return 0;
      }

              程序在 VS Code 中的运行结果如下所示:

              我们也可以用数学方法来解决这个问题,减少循环次数:

      设:

      • 10 元数量为 x
      • 5 元数量为 y
      • 2 元数量为 z
      • 1 元数量为 w

      则有:

      1. 10x + 5y + 2z + w = 100
      2. x + y + z + w = 40
      3. x, y, z, w ≥ 1

      由方程 2 可得:w = 40 - x - y - z ,代入方程 1:

      10x + 5y + 2z + (40 - x - y - z) = 100
      => 9x + 4y + z = 60

      现在我们需要找到满足:

      • x ∈ [1,9]
      • y ∈ [1, (60-9x-1)/4] (因为 z ≥ 1)
      • z = 60 - 9x - 4y 且 z ≥ 1
      #include <stdio.h>
      
      int main()
      {
          int count = 0; // 初始化计数器,用于记录符合条件的组合数
      
          // 遍历 10 元纸币的可能数量(x 的取值范围是 1 到 9,因为至少 1 张,最多 9 张)
          for (int x = 1; x <= 9; x++)
          {
              // 计算 5 元纸币的最大可能数量
              // 根据方程:9x + 4y + z = 60,且 z ≥ 1
              // 所以y的最大值为:(60 - 9x - 1) / 4
              for (int y = 1; y <= (60 - 9 * x - 1) / 4; y++)
              {
                  // 计算 2 元纸币的数量 z
                  // 根据方程:z = 60 - 9x - 4y
                  int z = 60 - 9 * x - 4 * y;
      
                  // 检查 z 是否合法(至少 1 张)
                  if (z >= 1)
                  {
                      // 计算 1 元纸币的数量 w
                      // 根据总张数方程:x + y + z + w = 40
                      // 所以 w = 40 - x - y - z
                      int w = 40 - x - y - z;
      
                      // 检查 w 是否合法(至少 1 张)
                      if (w >= 1)
                      {
                          // 如果所有条件都满足,增加计数器并打印当前组合
                          count++;
                          printf("组合 %d: 10元x%d, 5元x%d, 2元x%d, 1元x%d\n",
                                 count, x, y, z, w);
                      }
                  }
              }
          }
      
          // 打印符合条件的组合总数
          printf("总共有 %d 种符合条件的换法\n", count);
          return 0;
      }
      评论
      添加红包

      请填写红包祝福语或标题

      红包个数最小为10个

      红包金额最低5元

      当前余额3.43前往充值 >
      需支付:10.00
      成就一亿技术人!
      领取后你会自动成为博主和红包主的粉丝 规则
      hope_wisdom
      发出的红包

      打赏作者

      Thanks_ks

      你的鼓励将是我创作的最大动力

      ¥1 ¥2 ¥4 ¥6 ¥10 ¥20
      扫码支付:¥1
      获取中
      扫码支付

      您的余额不足,请更换扫码支付或充值

      打赏作者

      实付
      使用余额支付
      点击重新获取
      扫码支付
      钱包余额 0

      抵扣说明:

      1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
      2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

      余额充值