目录
1 break
1.1 介绍
1.2 流程图
1.3 在循环中使用 break
1.4 注意事项
1.5 案例:判断质数
2 continue
2.1 介绍
2.2 流程图
2.3 在循环中使用 continue
2.4 案例:逢七过游戏
3 goto 语句
3.1 介绍
3.2 基本语法
3.3 流程图
3.4 基本使用
3.5 死循环
3.6 案例:实现循环
4 编程综合练习
4.1 整数范围判断
4.1.1 scanf 的返回值
4.1.2 清除缓冲区
4.2 闰年判断
4.3 水仙花数判断
4.4 月份天数计算与闰年识别
4.5 星期判断与输出
4.6 遍历并打印指定范围内的闰年
4.7 字符序列打印
4.8 计算交错序列的和
4.9 判断整数的对称性(回文数)
4.10 计算 n 的阶乘 n!
4.11 人民币换零钱组合问题
5 测试题
1 break
1.1 介绍
break 语句用于终止某个语句块的执行,只能用于 switch 语句或者循环语句中。
在循环语句中:break 语句用于立即退出循环体(在嵌套循环中,break 只会退出最近的封闭循环),不管循环条件是否满足。一旦执行到 break 语句,循环就会终止,控制权会传递给循环之后的下一条语句。
在 switch 语句中:break 语句用于退出 switch 语句块。在 switch 语句中,break 通常用于防止代码自动地继续执行到下一个 case 标签,这是因为在 C 语言中,switch 语句的 case 分支是 “穿透” 的,即如果没有 break 语句,程序会继续执行下一个 case 分支的代码,直到遇到 break 或 switch 语句的末尾。
1.2 流程图
1.3 在循环中使用 break
目的:主要用于在满足特定条件时提前终止循环。
效果:当 break 语句被执行时,它会导致最近的封闭循环被立即终止,控制流跳转到循环之后的语句。
#include <stdio.h>
int main()
{
for (int i = 0; i < 10; i++)
{
// 检查 i 是否等于 3
if (i == 3)
{
// 如果 i 等于 3,则跳出循环
break;
}
// 如果 i 不等于 3,打印 i 的值
printf("%d \t", i); // 0 1 2
}
return 0;
}
在上面的示例代码中,break 语句用于在 i 等于 3 时退出 for 循环。因此,循环将只打印 i 的值从 0 到 2,当 i 达到 3 时,循环将不会继续执行,而是跳转到 return 0; 语句。
1.4 注意事项
break 语句只能用于循环或 switch 语句中。如果尝试在循环或 switch 之外使用 break,编译器将报错。
在 switch 语句中,break 用于防止代码自动 “落入” 下一个 case 语句(即,避免执行不需要的 case 分支)。
在嵌套循环中,break 只会退出最近的封闭循环。如果需要从多层嵌套的循环中退出,可以使用多个 break 或者考虑使用其他控制流语句(如标志变量、goto 语句,尽管 goto 通常不被推荐)。如下代码所示:
#include <stdio.h>
int main()
{
int i, j;
// 外层循环
for (i = 1; i <= 3; i++)
{
printf("外层循环:i = %d\n", i);
// 内层循环
for (j = 1; j <= 5; j++)
{
// 当 j 等于 4 时,退出内层循环
if (j == 4)
{
break; // 这里只会退出内层循环
}
printf(" 内层循环:j = %d\n", j);
}
// 注意:这里外层循环的迭代会继续进行
// 因为 break 只退出了内层循环
}
return 0;
}
在上面的示例代码中,外层循环遍历 i 的值从 1 到 3,而内层循环遍历 j 的值从 1 到 5。在内层循环中,有一个条件检查 j 是否等于 4。如果等于 4,则执行 break 语句,这会立即退出内层循环,但外层循环的迭代会继续进行。因此,输出将显示 j 的值从 1 到 3,然后当 j 达到 4 时,内层循环被退出,但外层循环会继续,并且内层循环会再次开始,从 j = 1 重新开始,直到外层循环也完成其所有迭代。
输出结果如下所示:
1.5 案例:判断质数
编写程序,要求输入一个数字,判断该数字是否是质数。
质数(Prime number)是大于 1 的自然数,除了 1 和它本身以外不再有其他因数的数。换句话说,一个大于 1 的自然数,如果只能被 1 和它本身整除,那么这个数就是质数。
#include <stdio.h>
#include <stdbool.h> // 引入布尔类型支持
/*
本程序使用了 _Bool 类型来表示布尔值,这是 C99 标准引入的。
在 C99 之前的版本中,通常使用 int 类型,并通过 0 和非 0 值来表示 false 和 true。
*/
int main()
{
// 获取输入的数字
int num;
printf("输入一个数字:");
scanf("%d", &num);
// 定义变量,用于标记是否为质数
// 大于 1 的数字先标记为真,再通过循环看是否推翻;小于 1 的数字直接标记为假
_Bool isPrime = num > 1 ? 1 : 0;
// 循环判断 num 是否为质数
// 从 2 遍历到 num 的一半(包含),如果 num 能被其中任何一个数整除,则不是质数
for (int i = 2; i <= num / 2; i++) // 可以优化到 num 的算术平方根
{
if (num % i == 0) // 如果 num 能被 i 整除
{
isPrime = 0; // 标记 num 不是质数
break; // 退出循环,因为已经找到了一个除数,无需继续检查
}
}
// 根据 isPrime 的值输出结果
if (isPrime)
{
printf("%d 是质数!", num);
}
else
{
printf("%d 不是质数!", num);
}
return 0;
}
如果不想使用 _Bool 类型(尽管它在 C99 及以后的版本中是完全有效的),可以使用普通的 int 类型来作为标志变量,并通过 0(表示 false)和非 0(通常使用 1 来表示 true)来管理这个标志。以下是修改后的代码,它使用了 int 类型的标志变量来替代 _Bool:
#include <stdio.h>
int main()
{
// 获取输入的数字
int num;
printf("输入一个数字:");
scanf("%d", &num);
// 定义变量,用于标记是否为质数
// 大于 1 的数字先标记为真(1),再通过循环看是否推翻;小于 1 的数字直接标记为假(0)
int isPrime = num > 1 ? 1 : 0;
// 循环判断 num 是否为质数
// 从 2 遍历到 num 的一半(包含),如果 num 能被其中任何一个数整除,则不是质数
for (int i = 2; i * i <= num; i++) // 优化:只需检查到 sqrt(num) 即可
{
if (num % i == 0) // 如果 num 能被 i 整除
{
isPrime = 0; // 标记 num 不是质数
break; // 退出循环,因为已经找到了一个除数,无需继续检查
}
}
// 根据 isPrime 的值输出结果
if (isPrime)
{
printf("%d 是质数!", num);
}
else
{
printf("%d 不是质数!", num);
}
return 0;
}
在这个修改后的版本中,isPrime 被声明为一个 int 类型的变量,并且通过 1(表示 true)和 0(表示 false)来设置其值。
此外,我还对质数检查的逻辑进行了一个小优化,即将循环的上限从 num / 2 改为 sqrt(num)。不过,由于 C 语言标准库中并没有直接计算平方根的函数返回整数类型,所以在这里我保留了 i * i <= num 作为条件,它等价于检查 i 是否小于或等于 num 的平方根。这样做可以避免浮点运算,同时保持逻辑的正确性。
如果确实需要调用平方根函数,可以使用 <math.h> 头文件中的 sqrt() 函数,但请注意,这将引入浮点数的精度问题,并且可能需要将 i 声明为 double 类型。然而,在这个特定的情况下,使用 i * i <= num 是一种更简洁且高效的方法。
注意:在检查质数时,我们实际上可以只遍历到 sqrt(num) 而不是 num/2,因为如果 num 有一个大于 sqrt(num) 的因数,那么它必定还有一个小于或等于 sqrt(num) 的因数。这个优化可以显著提高对于大数的质数检查的效率。
2 continue
2.1 介绍
continue 只能用在 for、while 或 do-while 循环中。
continue 用于跳过当前循环的剩余部分,并强制开始下一次循环的迭代。
continue 语句只影响它所在的那一层循环,如果在一个嵌套的循环中使用 continue,它将只跳过当前循环的剩余部分,并继续执行外层循环的下一次迭代。
与 break 的区别:continue 和 break 都用于控制循环的流程,但它们的作用不同。continue 跳过当前迭代的剩余部分,而 break 则完全退出循环。
2.2 流程图
2.3 在循环中使用 continue
目的:在某些条件下,可能不希望执行循环体中的全部代码。使用 continue 可以跳过当前迭代中 continue 之后的代码,直接开始下一次迭代;当知道某些情况下继续执行循环体的剩余部分将是徒劳的,使用 continue 可以避免这些无用的计算或操作,从而提高程序的执行效率。
效果:一旦 continue 语句被执行,循环体中 continue 之后的任何代码都将被忽略,循环控制将直接跳转到下一次迭代的开始;虽然 continue 不会改变循环的总迭代次数(除非它与循环条件或修改循环控制变量的代码结合使用),但它可以减少循环体中某些代码块的执行次数。
#include <stdio.h>
int main()
{
// 使用 for 循环遍历从 0 到 9 的数字
for (int i = 0; i < 10; i++)
{
// 判断当前数字是否等于 3
if (i == 3)
{
// 如果是 3,则执行 continue 语句,跳过当前循环的剩余部分,直接进入下一次循环迭代
continue;
}
// 如果 i 不等于3,则执行以下语句,打印当前数字并换行
printf("%d \t", i);
// 输出: 0 1 2 4 5 6 7 8 9
}
return 0;
}
在上面的示例代码中,for 循环从 0 开始,直到 9 结束,循环变量 i 每次循环都会增加 1。在循环体内,首先通过一个 if 语句检查i的值是否等于 3。如果等于 3,则执行 continue 语句,这将导致循环体中的剩余部分(即 printf 函数调用)被跳过,并立即开始下一次循环迭代。如果 i 的值不等于 3,则执行 printf 函数,打印出当前的 i 值并换行。因此,这个程序将打印出 0 到 9 之间的所有数字,但会跳过数字 3 的打印。
2.4 案例:逢七过游戏
输出 100 以内(包括100)的数字,跳过那些 7 的倍数或包含 7 的数字。
#include <stdio.h>
int main()
{
// 初始化一个 for 循环,从 1 遍历到 100(包含1 和 100)
for (int i = 1; i <= 100; i++)
{
// 逻辑判断部分
// i % 7 == 0 判断当前数字 i 是否是 7 的倍数
// i % 10 == 7 判断当前数字 i 的个位上是否带 7
// i / 10 == 7 判断当前数字 i 的十位上是否带 7
// 如果上述三个条件中的任意一个为真,则执行 continue 语句
if (i % 7 == 0 || i % 10 == 7 || i / 10 == 7)
{
// 当满足上述任一条件时,使用 continue 语句跳过当前循环的剩余部分
// 即跳过 printf 语句,直接开始下一次循环迭代
continue;
}
// 如果不满足上述条件,则执行 printf 语句,打印出当前数字 i
printf("%d ", i);
}
return 0;
}
输出结果如下所示:
注意:在循环的最后一行之前使用 continue 是多余的,因为它会立即导致循环进入下一次迭代,而循环的末尾代码(如果有的话)将不会被执行。
3 goto 语句
3.1 介绍
goto 语句是一个跳转语句,它允许程序无条件地跳转到同一函数内的另一个标签(label)处继续执行,标签(label)的名称可以自行设置,需要满足标识符规范。
goto 语句通常被认为是一种不良的编程实践,因为它可能导致代码难以理解和维护,特别是当程序变得复杂时。在开发中不建议使用 goto 语句,但我们需要掌握 goto 语句的执行流程,以能够看懂其他开发者的代码中可能出现的 goto 语句。
3.2 基本语法
goto 标签名 // 跳转到指定的标签(label)处
...
标签名: // 定义一个标签(label)
语句; // 跳转到这里
goto 后面如果引用了没有定义的标签,编译器会报错!
3.3 流程图
3.4 基本使用
#include <stdio.h>
int main()
{
printf("start \n");
// 使用 goto 语句跳转到标签 label1 处执行
goto label1; // label1 是标签名,这里会跳过下面的两条打印语句
// 下面的两条打印语句由于 goto 的存在,将不会被执行
printf("ok1 \n");
printf("ok2 \n");
label1:
// 跳转到这里执行
printf("ok3 \n");
printf("ok4 \n");
return 0;
}
输出结果如下所示:
3.5 死循环
在 C 语言中,使用 goto 语句可以很容易地创建一个死循环。死循环是指程序中的一段代码无限次地重复执行,而没有明确的退出条件。
在下面这个例子中,goto start; 语句将程序的控制流无条件地跳转到标签 start: 处,这导致 printf 语句无限次地重复执行,形成了一个死循环。
#include <stdio.h>
int main() {
start: // 定义一个标签名为 start
printf("这是一个死循环\n");
goto start; // 跳转到标签start处,形成死循环
// 注意:由于goto语句的存在,下面的代码将永远不会被执行
return 0;
}
下面是一个使用 goto 语句创建死循环的另一个例子,其中涉及两个标签互相跳转,形成一个无限循环。
#include <stdio.h>
int main()
{
// 定义两个标签
label1:
printf("正在跳转到 label2...\n");
goto label2; // 跳转到 label2
label2:
printf("已经到达 label2,准备跳回 label1...\n");
// 注意:这里没有使用任何条件来判断何时停止循环
// 因此,这将是一个无限循环
goto label1; // 跳回 label1
// 下面的代码由于无限循环的存在,将永远不会被执行
printf("这条语句永远不会被执行。\n");
return 0;
}
在实际编程中,我们应该避免创建无限循环,除非我们确实需要程序持续运行(例如,等待用户输入或监听网络事件)。如果确实需要无限循环,并且编译器发出警告,我们可以考虑使用 while(1) 循环或其他方法来明确表达我们的意图,而不是使用 goto 语句。
另外,请记得在需要的时候提供适当的退出机制,比如通过用户输入、特定条件或错误处理来跳出循环。
3.6 案例:实现循环
使用 goto 语句实现循环输出数字 1 ~ 5。
#include <stdio.h>
int main()
{
int i = 1; // 初始化计数器 i 为 1
start: // 定义一个标签 start,用于 goto 语句跳转
if (i <= 5) // 判断 i 是否小于或等于 5
{
printf("%d ", i); // 打印当前 i 的值
i++; // 将 i 的值增加 1
goto start; // 如果 i 仍然小于或等于 5,则跳转到标签 start 处继续执行
}
// 如果 i 大于 5,则不执行 goto 语句,程序继续向下执行到 return 语句
return 0;
}
注意:虽然 goto 语句在某些特定情况下(如错误处理和复杂的条件跳转)可能有用,但通常建议避免使用它,特别是用于创建循环结构。在大多数情况下,使用 for、while 或 do-while 循环是更清晰、更可维护的选择。
4 编程综合练习
4.1 整数范围判断
编写一个 C 语言程序,该程序要求用户输入一个整数,并根据输入的整数值判断它属于哪个范围:大于 0、小于 0 还是等于 0。然后,程序应该输出相应的信息来告知用户他们的输入属于哪个范围。
#include <stdio.h>
int main()
{
int number;
int scanResult; // 用于存储 scanf 的返回值,返回读取成功的个数
printf("请输入一个整数:");
// 尝试读取一个整数
// 将读取操作放在 while 循环中,可以一直读取数据
while ((scanResult = scanf("%d", &number)) != 1)
{
// 如果 scanf 没有读取到一个整数(即返回值不是 1 )
// 清除输入缓冲区中的残留输入
while (getchar() != '\n')
;
// 提示用户重新输入
printf("输入错误,请输入一个整数:");
}
// 现在我们已经成功读取了一个整数
if (number > 0)
{
printf("输入的整数大于0。\n");
}
else if (number < 0)
{
printf("输入的整数小于0。\n");
}
else
{
printf("输入的整数等于0。\n");
}
return 0;
}
输出结果如下所示:
4.1.1 scanf 的返回值
当 scanf 用于读取一个整数(例如,使用 "%d" 格式说明符)时,它会尝试从输入缓冲区中解析一个整数。如果成功,它会返回成功读取的项目数。如果失败(比如因为输入的不是一个整数),它会返回一个小于请求的项目数的值(在这个情况下是 0,因为请求的是 1 个整数但没有成功读取到)。
4.1.2 清除缓冲区
// 如果 scanf 没有读取到一个整数(即返回值不是 1 )
while (getchar() != '\n')
;
上面这段代码的目的是在 scanf 未能读取到一个整数时,清除输入缓冲区中残留的、导致 scanf 失败的非整数输入,以便程序可以重新尝试读取有效的整数输入。
下面是代码的详细解释:
while (getchar() != '\n'):这个循环会一直执行,直到 getchar() 函数读取到换行符(\n)为止。getchar() 函数从输入缓冲区中读取下一个可用的字符,并返回它。如果缓冲区中没有字符可供读取,程序将等待用户输入。
;:循环体是空的,这意味着每次循环迭代时实际上什么也不做,只是调用 getchar() 函数并丢弃它的返回值,直到遇到换行符。这样做是为了消耗掉输入缓冲区中所有导致 scanf 失败的字符,包括可能的非数字字符、空格、制表符等,直到用户按下回车键(此时换行符 \n 被放入输入缓冲区)。
通过这种方式,程序能够清除输入缓冲区中残留的无效输入,并准备好接收新的输入尝试。这通常是在需要循环读取用户输入直到获得有效输入时使用的技术之一。
4.2 闰年判断
编写一个 C 语言程序,该程序要求用户输入一个年份,并判断该年份是否为闰年。根据用户输入的年份,输出相应的判断结果,即告知用户该年份是闰年还是非闰年。
根据闰年的定义,一个年份如果满足以下条件之一,则被认为是闰年:
- 该年份能被 4 整除,但不能被 100 整除。
- 该年份能被 400 整除。
#include <stdio.h>
int main()
{
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;
}
4.3 水仙花数判断
编写一个 C 语言程序,该程序直接在 main 函数中实现判断一个整数是否为水仙花数的功能。水仙花数是一个三位数,其各位数字的立方和等于该数本身。例如,153 是一个水仙花数,因为 1^3 + 5^3 + 3^3 = 153。
#include <stdio.h>
int main()
{
int number, ge, si, bai, sum;
printf("请输入一个整数(三位数):");
scanf("%d", &number);
// 确保是三位数
if (number < 100 || number > 999)
{
printf("输入的不是一个三位数。\n");
return 1; // 返回一个非零值表示程序异常退出
}
// 分解数字
ge = number % 10; // 个位数
si = (number / 10) % 10; // 十位数
bai = number / 100; // 百位数
// 计算立方和
sum = ge * ge * ge + si * si * si + bai * bai * bai;
// 判断是否为水仙花数
if (sum == number)
{
printf("%d 是水仙花数。\n", number);
}
else
{
printf("%d 不是水仙花数。\n", number);
}
return 0;
}
4.4 月份天数计算与闰年识别
编写一个 C 语言程序,该程序能够接收用户输入的月份和年份,然后根据这些信息计算出该月份在指定年份中的天数。程序需要特别考虑闰年的情况,因为闰年的 2 月份有 29 天,而平年的 2 月份只有 28 天。
#include <stdio.h>
int main()
{
int month, year;
printf("请输入月份(1-12):");
scanf("%d", &month);
printf("请输入年份:");
scanf("%d", &year);
// 检查月份是否合法
if (month < 1 || month > 12)
{
printf("输入的月份不合法。\n");
return 1; // 非零返回值表示异常结束
}
// 初始化天数,默认为 31 天
int days = 31;
// 使用 switch 语句来判断月份并可能地修改天数
switch (month)
{
case 2: // 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:
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;
}
4.5 星期判断与输出
编写一个 C 语言程序,该程序能够接收用户输入的星期几(以数字形式表示,其中 1 代表星期一,7 代表星期日),然后根据输入的星期数字打印出相应的信息。具体要求如下:
- 如果用户输入的星期数字是 1 到 3(包含 1 和 3),则打印出 “AAA”。
- 如果用户输入的星期数字是 4 到 5(包含 4 和 5),则打印出 “BBB”。
- 如果用户输入的星期数字是 6 或 7,则打印出 “CCC”。
- 如果用户输入的星期数字不是 1 到 7 之间的整数,则打印出 “输入错误”。
#include <stdio.h>
int main()
{
int day;
printf("请输入星期几(1-7):");
scanf("%d", &day);
// 对输入进行合法性检查
if (day < 1 || day > 7)
{
printf("输入错误。\n");
}
else
{
switch (day)
{
case 1:
case 2:
case 3:
printf("AAA\n");
break;
case 4:
case 5:
printf("BBB\n");
break;
case 6:
case 7:
printf("CCC\n");
break;
// 注意:这里的 default 实际上是不需要的,因为所有可能的输入值都已经被覆盖
}
}
return 0;
}
4.6 遍历并打印指定范围内的闰年
编写一个 C 语言程序,该程序需要遍历从 1000 年到 999 9年(包含 1000 年和 9999 年)的所有年份,并判断每个年份是否为闰年。如果是闰年,则将该年份打印到控制台上。闰年的判断规则是:一个年份如果能被 4 整除且不能被 100 整除,或者能被 400 整除,则该年份是闰年。分别使用 for 循环、while 循环和 do-while 循环实现上述功能
#include <stdio.h>
int main() {
int year;
for (year = 1000; year <= 9999; year++) {
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
printf("%d\n", year);
}
}
return 0;
}
#include <stdio.h>
int main() {
int year = 1000;
while (year <= 9999) {
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
printf("%d\n", year);
}
year++;
}
return 0;
}
#include <stdio.h>
int main() {
int year = 1000;
do {
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
printf("%d\n", year);
}
year++;
} while (year <= 9999);
return 0;
}
4.7 字符序列打印
编写一个 C 语言程序,该程序需要完成以下任务:
- 打印小写字母:首先,程序需要按顺序打印出所有的小写英文字母(a 到 z)。
- 打印大写字母逆序:接着,程序需要以逆序的方式(即从 Z 到 A)打印出所有的大写英文字母。
#include <stdio.h>
int main() {
// 输出小写字母 a-z
for(char c = 'a'; c <= 'z'; c++) {
printf("%c ", c);
}
printf("\n"); // 换行
// 输出大写字母 Z-A
for(char c = 'Z'; c >= 'A'; c--) {
printf("%c ", c);
}
printf("\n"); // 换行
return 0;
}
4.8 计算交错序列的和
编写一个 C 语言程序,该程序需要计算并输出序列 1 - 1/2 + 1/3 - 1/4 + ... + 1/n 的和,其中 n 是一个用户指定的正整数(例如,n = 100)。序列中的每一项的符号交替出现,即第一项为正,第二项为负,第三项为正,以此类推。
#include <stdio.h>
int main() {
double sum = 0.0; // 用来存储序列的和
int sign = 1; // 用来表示当前的符号,初始化为正(1)
for (int i = 1; i <= 100; i++) {
// 将当前项加到总和中,注意将 1 转换为 1.0 以避免整数除法
sum += sign * 1.0 / i;
// 改变符号,为下一次迭代做准备
sign = -sign;
}
printf("The sum of the series is: %f\n", sum);
return 0;
}
4.9 判断整数的对称性(回文数)
输入一个整型数,判断是否是对称数,如果是,输出 yes,否则输出 no,不用考虑这个整型数过大,int 类型存不下,不用考虑负值;例如 12321 是对称数,输出 yes,124421 是对称数,输出 yes,1231 不是对称数,输出 no。
#include <stdio.h>
int main()
{
// inputNumber : 用于存储输入的数字
// reversedNumber : 用于存储反转后的数字,必须初始化,后需要对其做乘 10 处理
// tempNumber : 临时存储输入的数字,用于后续比较
int inputNumber, reversedNumber = 0, tempNumber;
// 从用户处获取输入的数字
printf("请输入一个整数:");
scanf("%d", &inputNumber);
// 保存原始数字,以便后续比较
tempNumber = inputNumber;
// 反转数字
while (inputNumber != 0)
{
// // 将当前数字的个位添加到反转数字中,并乘以 10 为下一位做准备
reversedNumber = reversedNumber * 10 + inputNumber % 10;
// 去除当前数字的个位
inputNumber /= 10;
}
// 判断输入的数字是否与反转后的数字相等
if (tempNumber == reversedNumber)
{
printf("yes"); // 是对称数,输出"yes"
}
else
{
printf("no"); // 不是对称数,输出"no"
}
return 0;
}
4.10 计算 n 的阶乘 n!
利用 while 或者 for 循环计算 n! 的值。
#include <stdio.h>
int main() {
int number,factorial = 1; // 初始化阶乘结果为 1
// 读取用户输入的数
scanf("%d", &number);
// 使用 for 循环计算阶乘
for(int i = 1; i <= number; i++) {
factorial *= i; // 累乘计算阶乘
}
// 输出结果
printf("%d\n", factorial);
return 0;
}
4.11 人民币换零钱组合问题
题目描述:
某人想将手中的一张面值 100 元的人民币换成 10 元、5 元、2 元和 1 元面值的票子,要求换正好 40 张,并且每种票子至少各有一张。请问有多少种不同的换法?
题目分析:
为了更高效地解决这个问题,我们需要考虑每种面额纸币的数量范围,并据此设置循环的边界条件。具体地,我们可以这样设定:
- 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 counter = 0; // 用于计数满足条件的换法
// 外层循环:10 元纸币的数量,从 1 到 9 张
for (int i = 1; i <= 9; ++i) {
// 注释:10 元纸币的数量不能为 0(至少 1 张),且不能超过 9 张(否则总金额会超过 100 元)
// 第二层循环:5 元纸币的数量,从 1 到 17 张
for (int j = 1; j <= 17; ++j) {
// 注释:5 元纸币的数量也不能为 0(至少 1 张),(100-10-2-1)/5=17
// 第三层循环:2 元纸币的数量,从 1 到 37 张
for (int k = 1; k <= 37; ++k) {
// 注释:2 元纸币的数量也不能为 0(至少 1张),最多 37 张
// 第四层循环:1 元纸币的数量,从 1 到 37 张
for (int l = 1; l <= 37; ++l) {
// 注释:1 元纸币的数量也不能为 0(至少 1 张),最多 37 张
// 检查当前组合是否满足总金额为 100 元和总张数为 40 张的条件
if (100 == i * 10 + j * 5 + k * 2 + l * 1 && 40 == i + j + k + l) {
counter++; // 如果满足条件,则计数器加 1
}
}
}
}
}
// 输出满足条件的换法总数
printf("%d\n", counter);
return 0;
}
上面代码可以通过减少不必要的循环来优化。关键在于,一旦选择了 10 元、5 元和 2 元纸币的数量后,1 元纸币的数量就可以直接计算出来,而不需要额外的循环。此外,5 元和 2 元纸币的最大数量也需要根据已经选择的 10 元纸币数量以及剩余需要填补的金额和张数来动态调整。
下面是一个优化后的版本:
#include <stdio.h>
int main()
{
int counter = 0; // 用于计数满足条件的换法
// 外层循环:10 元纸币的数量,从 1 到 9 张
for (int i = 1; i <= 9; ++i)
{
// 剩余金额和剩余张数
int remaining_amount = 100 - i * 10;
int remaining_count = 40 - i;
// 第二层循环:5 元纸币的数量
for (int j = 1; j <= remaining_amount / 5 && j <= remaining_count - 2; ++j)
{
// 剩余金额和剩余张数
remaining_amount -= j * 5;
remaining_count -= j;
// 第三层循环:2 元纸币的数量
for (int k = 1; k <= remaining_amount / 2 && k <= remaining_count - 1; ++k)
{
// 剩余金额和剩余张数
remaining_amount -= k * 2;
remaining_count -= k;
// 计算 1 元纸币的数量
int l = remaining_amount; // 剩余金额即为 1 元纸币的数量
if (remaining_count == l)
{ // 检查剩余张数是否与1元纸币数量相等
counter++; // 如果满足条件,则计数器加 1
}
remaining_amount += k * 2; // 恢复剩余金额
remaining_count += k; // 恢复剩余张数
}
remaining_amount += j * 5; // 恢复剩余金额
remaining_count += j; // 恢复剩余张数
}
}
// 输出满足条件的换法总数
printf("%d\n", counter);
return 0;
}
注意:在进行数据处理之前,进行详尽且准确的数据验证是至关重要的,以确保数据的质量、完整性和准确性,从而支持更可靠的分析和决策过程。如上面编程练习中对用户输入的数据基本上都进行了正确性校验。
5 测试题
1. 请写出下面程序的运行结果:
int sum = 0;
for (int i = 1; i < 5; i++)
{
if (i == 3)
{
continue;
}
sum += i;
}
printf("%d", sum);
【答案】7
【解析】
(1)当 i 等于 1 时,sum 变为 1。
(2)当 i 等于 2 时,sum 变为 1 + 2 = 3。
(3)当 i 等于 3 时,由 于continue 语句,循环体内的 sum += i; 不会执行,所以 sum 保持不变为 3。
(4)当 i 等于 4 时,sum 变为 3 + 4 = 7。
因此,最终的输出是:7