实验3_C语言函数应用编程
一、实验目的
1. 能正确使用c语法规则定义、声明、调用函数
2. 能正确编写递归函数
3. 针对具体问题场景,能合理抽象出独立的功能模块,正确定义函数并使用,使得代码更具可读性、可维护性
4. 针对具体问题场景,能正确、合理使用全局变量和局部static变量,解决实际问题
二、实验过程
1、task1:成绩等级转换
源代码:
1 #include <stdio.h> 2 3 char score_to_grade(int score); // 函数声明 4 5 int main() { 6 int score; 7 char grade; 8 9 while (scanf_s("%d", &score) != EOF) { 10 grade = score_to_grade(score); // 函数调用 11 printf("分数: %d, 等级: %c\n\n", score, grade); 12 } 13 14 return 0; 15 } 16 17 // 函数定义 18 char score_to_grade(int score) { 19 char ans; 20 21 switch (score / 10) { 22 case 10: 23 case 9: ans = 'A'; break; 24 case 8: ans = 'B'; break; 25 case 7: ans = 'C'; break; 26 case 6: ans = 'D'; break; 27 default: ans = 'E'; 28 } 29 30 return ans; 31 }
运行结果:

问题回答:
问题1:函数 score_to_grade 的功能是什么?形参类型、返回值类型是什么?
答:将输入的分数(score)转换为对应的等级(grade)。根据分数的区间,它将分数分为A,B,C,D或E等级。
形参类型:该参数为 int 类型,表示分数。返回值类型:函数返回一个 char 类型的值,表示对应分数的等级(如 'A', 'B', 'C', 'D', 或 'E')。
问题2:如果line21-28以下形式,代码存在哪些问题,请逐一指出。
1 switch(score/10) { 2 case 10: 3 case 9: ans = "A"; 4 case 8: ans = "B"; 5 case 7: ans = "C"; 6 case 6: ans = "D"; 7 default: ans = 'E'; 8 }
答:① 字符串和字符的类型不匹配:ans = "A"; 等行使用的双引号表示字符串类型,而字符类型应该使用单引号,例如 ans = 'A';
② 缺少 break 语句:在每个 case 后面缺少 break 语句,满足某个 case 条件后,代码会继续执行后面的所有 case ,直到遇到 break 或 switch 结束,这将导致错误的等级被赋值。例如,如果 score 是 89,程序会执行到 case 8;并给 ans 赋值为 B ,然后继续执行,最后会赋值为 E 。
2、task2:计算一个整数 n 所有数字的和
源代码:
1 #include <stdio.h> 2 int sum_digits(int n); // 函数声明 3 int main() { 4 int n; 5 int ans; 6 while (printf("Enter n: "), scanf_s("%d", &n) != EOF) { 7 ans = sum_digits(n); // 函数调用 8 printf("n = %d, ans = %d\n\n", n, ans); 9 } 10 return 0; 11 } 12 13 // 函数定义 14 int sum_digits(int n) { 15 int ans = 0; 16 while (n != 0) { 17 ans += n % 10; 18 n /= 10; 19 } 20 return ans; 21 }
运行结果:

问题回答:
问题1:函数sum_digits的功能是什么?
答:计算并返回一个整数 n 的所有数字的和。例如,给定 n = 123,该函数会返回 1 + 2 + 3 = 6。它通过逐位提取数字(使用 n % 10)并累加这些数字实现这个功能,直到 n 减少到 0 为止。
问题2:如果保持main代码和函数sum_digits声明不变,把函数sum_digits定义成如下实现方式,能实现同 等的效果吗? 如果不能实现同等效果,分析原因。 如果能实现同等效果,说明两种实现方式背后的算法思维区别。
1 int sum_digits(int n) { 2 if(n < 10) 3 return n; 4 5 return sum_digits(n/10) + n%10; 6 }
答:能实现同等效果。第一种是迭代方式:使用 while 循环,通过逐位提取数字的方式(n % 10)来累加数字,直到 n 变为 0。 这种方式在逻辑上是通过直接操作数的每一位来处理问题,通常更容易理解,且避免了递归的调用开销。 第二种是递归方式:使用递归方法 essentially calling itself with a smaller number (i.e., n / 10) until the number is less than 10. 这种方式有助于利用递归的特性,从数学角度思考问题,将复杂问题分解为更简单的部分。 递归实现可能在某些情况下易于表达(如单一表达式),但在深度递归时可能会因栈溢出而遭遇性能问题,尤其是对于大数字。
3、task3:计算整数 x 的 n 次方
源代码:
1 #include <stdio.h> 2 3 int power(int x, int n); // 函数声明 4 5 int main() { 6 int x, n; 7 int ans; 8 9 while (printf("Enter x and n: "), scanf_s("%d%d", &x, &n) != EOF) { 10 ans = power(x, n); // 函数调用 11 printf("n = %d, ans = %d\n\n", n, ans); 12 } 13 14 return 0; 15 } 16 17 // 函数定义 18 int power(int x, int n) { 19 int t; 20 21 if (n == 0) 22 return 1; 23 else if (n % 2) 24 return x * power(x, n - 1); 25 else { 26 t = power(x, n / 2); 27 return t * t; 28 } 29 }
运行结果:

问题回答:
问题1: 函数power的功能是什么?
答:函数 power 的功能是计算给定整数 x 的 n 次方,即 xn。
问题2:函数power是递归函数吗?如果是,找出递归模式。写出这个递归模式对应的数学公式模型。
答:函数 power 是一个递归函数。数学公式模型:

4、task4:打印100以内的孪生素数及其总数
源代码:
1 #include <stdio.h> 2 3 // 函数声明 4 int is_prime(int n); 5 6 int main() { 7 int n; 8 int count = 0; // 孪生素数的总数 9 printf("100以内的孪生素数有:\n"); 10 11 // 检查2到97的数字,判断对应的n和n+2是否为素数 12 for (n = 2; n <= 97; n++) { 13 if (is_prime(n) && is_prime(n + 2)) { 14 printf("(%d, %d)\n", n, n + 2); 15 count++; // 计数 16 } 17 } 18 19 // 输出孪生素数对的总数 20 printf("100以内的孪生素数对共有%d个。\n", count); 21 return 0; 22 } 23 24 // 判断一个正整数n是否是素数 25 int is_prime(int n) { 26 if (n <= 1) { 27 return 0; // 小于等于1的数字不是素数 28 } 29 for (int i = 2; i * i <= n; i++) { // 检查到平方根 30 if (n % i == 0) { 31 return 0; // 不是素数 32 } 33 } 34 return 1; // 是素数 35 }
运行结果:

5、task5:汉诺塔
源代码:
1 #include <stdio.h> 2 3 void move(int n, char source, char target, char auxiliary, int* count) { 4 if (n == 1) { 5 // 移动一个盘子并增加移动次数 6 printf("1: %c --> %c\n", source, target); 7 (*count)++; 8 } 9 else { 10 // 递归移动n-1个盘子 11 move(n - 1, source, auxiliary, target, count); 12 // 移动最后一个盘子 13 printf("%d: %c --> %c\n", n, source, target); 14 (*count)++; 15 // 递归移动n-1个盘子 16 move(n - 1, auxiliary, target, source, count); 17 } 18 } 19 20 int main() { 21 int n; 22 while (1) { 23 printf("请输入盘子数量 : "); 24 scanf_s("%d", &n); 25 if (n == 0) { 26 break; 27 } 28 29 int moveCount = 0; // 初始化移动次数 30 31 printf("盘子数量: %d\n", n); 32 move(n, 'A', 'C', 'B', &moveCount); // 从A到C,使用B作辅助 33 printf("一共移动了%d次。\n\n", moveCount); 34 } 35 36 return 0; 37 }
运行结果:

6、task6:分别用迭代方式和递归方式编写函数计算组合数
源代码:
1 #include <stdio.h> 2 3 int func(int n, int m); // 函数声明 4 5 int main() { 6 int n, m; 7 int ans; 8 9 while (scanf_s("%d%d", &n, &m) != EOF) { 10 ans = func(n, m); // 函数调用 11 printf("n = %d, m = %d, ans = %d\n\n", n, m, ans); 12 } 13 14 return 0; 15 } 16 17 // 迭代方式实现组合数计算 18 int combination_iterative(int n, int m) { 19 if (m > n || m < 0) { 20 return 0; 21 } 22 if (m == 0 || m == n) { 23 return 1; 24 } 25 26 int result = 1; 27 28 // 计算 C(n, m) = n! / (m! * (n - m)!) 29 for (int i = 0; i < m; i++) { 30 result = result * (n - i) / (i + 1); 31 } 32 33 return result; 34 } 35 36 // 递归方式实现组合数计算 37 int combination_recursive(int n, int m) { 38 if (m > n || m < 0) { 39 return 0; 40 } 41 if (m == 0 || m == n) { 42 return 1; 43 } 44 // C(n, m) = C(n-1, m-1) + C(n-1, m) 45 return combination_recursive(n - 1, m - 1) + combination_recursive(n - 1, m); 46 } 47 48 // 主函数实现 49 int func(int n, int m) { 50 // 可以在这里选择使用哪种实现方法 51 // return combination_iterative(n, m); // 使用迭代方式 52 return combination_recursive(n, m); // 使用递归方式 53 }
运行结果:

7、task7:用穷举法计算三个整数的最大公约数
源代码:
1 #include <stdio.h> 2 3 // 函数声明 4 int gcd(int a, int b, int c); 5 6 int main() { 7 int a, b, c; 8 int ans; 9 printf("请输入三个整数 (输入 Ctrl+D 或 Ctrl+Z 结束):\n"); 10 while (scanf_s("%d%d%d", &a, &b, &c) != EOF) { 11 ans = gcd(a, b, c); // 函数调用 12 printf("最大公约数: %d\n\n", ans); 13 } 14 return 0; 15 } 16 17 // 函数定义 18 int gcd(int a, int b, int c) { 19 int min_num = a; 20 if (b < min_num) min_num = b; 21 if (c < min_num) min_num = c; 22 23 for (int i = min_num; i > 0; i--) { 24 if (a % i == 0 && b % i == 0 && c % i == 0) { 25 return i; // 找到最大公约数后立即返回 26 } 27 } 28 return 1; // 如果没有找到大于1的公约数,返回1 29 }
运行结果:

三、实验心得
1、双引号表示字符串类型,单引号表示字符类型。
2、写程序先搭好框架,再按照算法填入相应的代码,运行调试。
3、程序错误大致有三种:算法错误、语法错误、运行结果与预期不符。
浙公网安备 33010602011771号