V2.0
C++基础教程<br />——<br />作业及参考答案全部汇总文档<br/>节3函数阶段作业<br/><br/>最新版本V2.0
<br>王道C++团队<br/>COPYRIGHT ⓒ 2021-2024. 王道版权所有基础题篇函数基础语法练习题函数基础语法练习题2变量种类/值传递等读程序题交互式简易计算器-函数/全局变量递归练习题扩展题篇扩展:打印目标步骤汉诺塔移动轨迹The End
Gn!
下面都是一些基础的语法、概念编程练习题。
Gn!
编写函数实现以下功能:
键盘录入一个正整数,请判断它是否是一个素数,然后控制台输出对应的结果。要对键盘录入的数据做参数校验,素数是一个大于1的自然数,它仅能被1和自身整除。
键盘录入两个整数:底(base)和幂指数(exponent),计算base的exponent次幂,并打印输出对应的结果。(注意底和幂指数都可能是负数)
提示:求幂运算时,基础的思路就是先无脑把指数转换成正数,然后累乘,最后再根据指数是否是负数决定是否取倒数。
非常简单且基础的语法练习题,如果这种题目都还有疑惑,说明代码量几乎为0,语法也几乎不熟悉,建议直接看参考代码先熟悉语法。
参考代码如下:
参考代码:
第一题,判断素数:
xxxxxxxxxx
3612345
6bool is_prime(int num) {
7// 如果num是1甚至比1还小,那它一定不是素数
8if (num <= 1) {
9return false;
10}
11// 将num从2开始除,一直除到该数的平方根
12for (int i = 2; i <= sqrt(num); i++) {
13if (num % i == 0) {
14// 此过程中只要有一个除尽了,那么就不是素数
15return false;
16}
17
18}
19// 没有被除尽,说明是素数
20return true;
21}
22
23int main(void) {
24int num;
25printf("请输入一个整数: ");
26scanf("%d", &num);
27
28if (is_prime(num)) {
29printf("%d是一个素数!\n", num);
30}
31else {
32printf("%d不是一个素数!\n", num);
33}
34
35return 0;
36}
第二题参考代码:
xxxxxxxxxx
351// 计算 base 的 exponent 次幂 - 循环累乘求解
2double power(int base, int exponent) {
3double result = 1.0; // 存储计算结果
4// 若幂指数是0,则直接返回1
5if (exponent == 0) {
6return result;
7}
8/*
9在计算幂运算的过程中
10幂指数有可能是负数,但为了简化计算可以先将指数转换成正数求解
11计算完毕后,若指数是一个负数则取倒数就可以了
12*/
13int positive_exponent = exponent > 0 ? exponent : -exponent; // 确保幂指数是一个正数
14
15// 累乘 base
16for (int i = 0; i < positive_exponent; i++) {
17result *= base;
18}
19// 如果原幂指数是一个负数,则取倒数
20if (exponent < 0) {
21result = 1.0 / result;
22}
23return result;
24}
25
26int main(void) {
27int base, exponent;
28printf("请输入底数和指数(用空格分隔):");
29scanf("%d %d", &base, &exponent);
30
31double result = power(base, exponent);
32// 为了更好的显示负指数的小数结果,可以保留更多小数点后面的有效数字
33printf("%d 的 %d 次幂是 %.20lf\n", base, exponent, result);
34return 0;
35}
以上。
Gn!
键盘录入三个边长(整数即可),然后用海伦公式计算三角形的面积(如果它确实是一个三角形的话)
海伦公式求三角形面积:
要求基于下列两个函数完成这个编程题:
xxxxxxxxxx
51// 判断abc是否可以组成三角形,true表示可以组成,false表示不可以
2bool is_triangle(int a, int b, int c);
3
4// 利用海伦公式在abc可以构成三角形的前提下,求三角形面积
5double get_area(int a, int b, int c);
注意:不要忘记使用sqrt函数要包含头文件<math.h>
非常简单且基础的语法练习题,如果这种题目都还有疑惑,说明代码量几乎为0,语法也几乎不熟悉,建议直接看参考代码先熟悉语法。
参考代码如下:
参考代码:
xxxxxxxxxx
3912345
6
7// 判断abc是否可以组成三角形,true表示可以组成,false表示不可以
8bool is_triangle(int a, int b, int c);
9
10// 利用海伦公式在abc可以构成三角形的前提下,求三角形面积
11double get_area(int a, int b, int c);
12
13bool is_triangle(int a, int b, int c) {
14// 任意两边之和必须大于第三边
15return (a + b > c) && (a + c > b) && (b + c > a);
16}
17
18double get_area(int a, int b, int c) {
19double s = (a + b + c) / 2.0; // 计算半周长
20return sqrt(s * (s - a) * (s - b) * (s - c)); // 使用海伦公式计算面积
21}
22
23int main(void) {
24int a, b, c; // 三角形的三个边长
25printf("请您键盘输入三角形的三个边长:");
26scanf("%d%d%d", &a, &b, &c);
27
28if (!is_triangle(a, b, c)){
29// 不能组成三角形
30printf("Error: 无法组成三角形!\n");
31return -1;
32}
33
34// 可以组成三角形,于是计算三角形的面积
35double area = get_area(a, b, c);
36printf("三角形的面积是: %.2lf\n", area);
37
38return 0;
39}
以上。
Gn!
读下列一段程序代码,回答以下一些问题。
xxxxxxxxxx
3912
3static int global = 1; // 全局变量
4
5void modify(int param) {
6static int static_local = 1; // 静态局部变量
7int local = 1; // 普通局部变量
8
9// 修改函数传参的值
10param *= 2;
11// 修改全局变量
12global *= 2;
13static_local *= 2; // 修改静态局部变量
14local *= 2; // 修改普通局部变量
15
16printf("test函数内部:param=%d, global=%d, static_local=%d, local=%d\n",
17param, global, static_local, local);
18}
19
20void modify_global(int global) {
21global = 100;
22}
23
24int main(void) {
25int param_a = 10;
26int param_b = 20;
27printf("初始值:param_a=%d, param_b=%d, global=%d\n\n", param_a, param_b, global);
28
29modify(param_a);
30printf("第一次调用后:param_a=%d, global=%d\n\n", param_a, global);
31
32modify(param_b);
33printf("第二次调用后:param_b=%d, global=%d\n\n", param_b, global);
34
35modify_global(global);
36printf("after modify_global global = %d\n", global);
37
38return 0;
39}
问题一:
通过modify函数调用,能不能修改"param_a"和"param_b"的取值呢?为什么?
问题二:
能不能在main函数当中访问静态局部变量"static_local"呢?为什么?
问题三:
全局变量"global"使用static修饰,这里的static关键字是什么作用?
问题四:
modify函数的两次调用中,"printf"输出打印的自身局部变量"local"的取值是一样的吗?为什么?
问题五:
modify函数内部修改了全局变量"global"的取值,两次调用"printf"函数输出的结果是什么?为什么?
问题六:
modify函数内部修改了静态局部变量"static_local"的取值,两次调用"printf"函数输出的结果是什么?为什么?
问题七:
modify_global函数希望修改"global"全局变量的取值,可以修改成功吗?为什么?
参考回答如下:
问题一:
通过
modify
函数调用,能不能修改param_a
和param_b
的取值呢?为什么?答:不能修改。
因为 C 语言中函数参数是值传递,实参的值会被拷贝一份传递给形参
param
,函数内部对param
的任何修改都只作用于这份拷贝,而不会影响原变量param_a
或param_b
的值。问题二:
能不能在
main
函数当中访问静态局部变量static_local
呢?为什么?答:不能访问。
因为
static_local
是定义在test
函数 内部的静态局部变量,它的作用域仅限于该函数内部,main 函数无法访问到它,因为并不在作用域范围内。即便静态局部变量生命周期贯穿整个程序运行期间,但它依然无法在函数外部被访问,这就是作用域产生的影响。问题三:
全局变量
global
使用static
修饰,这里的static
关键字是什么作用?答:将该全局变量的作用域限制在当前问题内部,而不再是整个工程全局都可以访问的了。
也就是说,
static int global = 1;
这个变量只能在当前源文件中使用,不能被其他文件访问。在这个语法中,static非常类似C++面向对象封装机制的访问权限修饰符,即private私有权限修饰符,用于限制变量的外部可见性。
问题四:
modify
函数的两次调用中,printf
输出打印的自身局部变量local
的取值是一样的吗?为什么?答:是一样的,都是2。
在
modify
函数中,local
是一个普通局部变量,每次调用函数时都会重新创建并初始化为 1,然后执行local *= 2
。而不同的函数调用之间,局部变量是独立不互相影响的。
所以不管调用多少次这个函数,其结果都是"local = 2"。
问题五:
modify
函数内部修改了全局变量global
的取值,两次调用printf
函数输出的结果是什么?为什么?答:
第一次调用后,
global
由 1 变为 2(global *= 2
);第二次调用后,
global
再次变为 4。原因:
global
是一个全局变量,在函数中直接访问并修改,每次调用函数都会让其值翻倍,且值在全局作用域内持续保留。问题六:
modify
函数内部修改了静态局部变量static_local
的取值,两次调用printf
函数输出的结果是什么?为什么?答:
第一次调用:
static_local = 1 × 2 = 2
第二次调用:
static_local = 2 × 2 = 4
原因:
static_local
是一个静态局部变量,其生命周期贯穿整个程序运行过程,在同一个函数的多次函数调用间保持上次的值,所以每次调用会继续累积变化。问题七:
modify_global
函数希望修改global
全局变量的取值,可以修改成功吗?为什么?答:不能修改成功!
modify_global
函数中的参数名是global
,它与全局变量同名,会屏蔽(隐藏)全局变量,函数内部操作的是这个形参的副本,而不是全局变量本身。再加上是值传递,修改形参也不会影响实参,所以全局变量不会被改变。
以上。
Gn!
实现一个终端交互式的简易计算器,交互的形式大体如下:
要求至少提供四种运算加减乘除,如下:
x1int add(int a, int b);
2int subtract(int a, int b);
3int multiply(int a, int b);
4float divide(int a, int b);
并且在结束进程时,打印总共执行操作的次数。(也就是这些函数调用的次数)
注意:除法的实现,要求判断除数不为0,并且在除数为0时使用exit表示异常退出进程。
要求:需要编写一个函数,用于随时随地打印当前操作的执行次数,也就是打印上述四个函数的调用次数。
思考一下:这个变量应该设置为什么变量呢?
参考代码如下:
参考代码:
x1234
5// 全局变量,用于记录操作次数
6// 设置为static修饰的全局变量,缩小作用域,更加安全
7static int operation_count = 0;
8
9// 函数声明
10int add(int a, int b);
11int subtract(int a, int b);
12int multiply(int a, int b);
13float divide(int a, int b);
14void print_operation_count(void);
15
16int main(void) {
17int a, b;
18char operator;
19char choice;
20
21do {
22printf("请输入要计算的表达式(例如,5 + 3): ");
23scanf(" %d %c %d", &a, &operator, &b);
24
25switch (operator) {
26case '+':
27printf("结果: %d\n", add(a, b));
28break;
29case '-':
30printf("结果: %d\n", subtract(a, b));
31break;
32case '*':
33printf("结果: %d\n", multiply(a, b));
34break;
35case '/':
36printf("结果: %.2f\n", divide(a, b));
37break;
38default:
39printf("无效的运算符。\n");
40}
41// 询问用户是否继续
42printf("是否继续? (y/n): ");
43scanf(" %c", &choice);
44printf("\n");
45} while (choice == 'y' || choice == 'Y');
46
47print_operation_count();
48return 0;
49}
50
51int add(int a, int b) {
52operation_count++;
53return a + b;
54}
55
56int subtract(int a, int b) {
57operation_count++;
58return a - b;
59}
60
61int multiply(int a, int b) {
62operation_count++;
63return a * b;
64}
65
66float divide(int a, int b) {
67if (b == 0) {
68printf("error:除数为零!\n");
69exit(1);
70}
71operation_count++;
72return (float)a / b;
73}
74
75void print_operation_count(void) {
76printf("总共执行的操作次数为: %d次\n", operation_count);
77}
以上。
Gn!
(1) 汉诺塔
有三根杆子A,B,C。A杆上有 N 个 (N>1) 穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至 C 杆:
每次只能移动一个圆盘;
大盘不能叠在小盘上面。
利用递归的方式,打印出移动轨迹。运行结果如下图所示:
(2) 十进制转换成二进制
给定任意一个非负十进制整数,请利用递归的方式,求解它的二进制表示方式
基本的思路是:把该整数除以2得到余数,然后倒着输出余数。
思考一下:如何实现倒着打印余数呢?
参考代码如下:
参考代码如下:
第一题,求解汉诺塔移动轨迹,参考代码:
x123
4/*
5递归求解汉诺塔的移动轨迹
6假如有三座塔:A B C
7
8扩展:
9给定n个盘子,然后再给一个整数m,请你打印m + 1时,汉诺塔问题的移动轨迹
10*/
11
12// n个盘子的时候 start是原始塔 target是目标塔 sup是辅助塔
13void move(int n, char start, char target, char sup) {
14// 递归的出口
15// 当n=1时,直接将start上的唯一一个最大的盘子,移动到target上去
16if (n == 1) {
17printf("%c --> %c\n", start, target);
18return;
19}
20// 递归体
21// 1.第一步:把n-1个盘子从start移动到sup,这个过程需要target辅助
22// 第一步的n-1个盘子的时候 start是原始塔 sup是目标塔 target是辅助塔
23move(n - 1, start, sup, target);
24
25// 2.第二步:把最大的盘子从start移动到target
26printf("%c --> %c\n", start, target);
27
28// 3.第三步:把n-1个盘子从sup移动到target,这个过程需要start辅助
29// 第三步的n-1个盘子的时候 sup是原始塔 target是目标塔 start是辅助塔
30move(n - 1, sup, target, start);
31}
32
33void hanoi_track(int n) {
34long long steps = (1LL << n) - 1;
35printf("%d个盘子的汉诺塔问题,至少需要移动%lld步完成!\n", n, steps);
36
37// 该函数不能用于直接递归打印移动轨迹,所以需要一个辅助函数来完成递归,这种写法在递归中特别常见
38/*
39第二个参数是盘子的原始位置
40第三个参数是盘子移动完成汉诺塔问题的目标位置
41第四个参数是完成n个盘子汉诺塔问题时,需要的辅助塔
42*/
43move(n, 'A', 'C', 'B');
44}
45int main(void) {
46// 4表示汉诺塔问题中盘子的个数
47hanoi_track(4);
48return 0;
49}
第二题,十进制转换成二进制参考代码:
xxxxxxxxxx
37123
4void decimal_to_binary(unsigned int n) {
5// 递归的出口
6// 0和1的十进制二进制表示是一样的
7if (n == 0 || n == 1) {
8printf("%u", n);
9return; // 直接返回,不再递归
10}
11
12// 递归体
13// 只要n不是0或1,那就递归调用除以2
14decimal_to_binary(n / 2);
15
16// 打印余数
17/*
18十进制转换成二进制
19最终要将余数进行倒读
20如何实现倒读余数呢?
21只需要将递归体放在打印余数的上面即可
22
23这种写法利用了栈的先进后出的特点
24先入栈也就是先求余数的,更大的数,反而最后打印余数
25后入栈也就是后求余数的,更小的数,反而最先打印余数
26这样就实现倒读余数
27*/
28printf("%u", n % 2);
29}
30
31
32int main(void) {
33decimal_to_binary(777);
34printf("\n");
35
36return 0;
37}
以上。
Gn!
以下题目都属于扩展题。
Gn!
汉诺塔移动轨迹的扩展题,感兴趣且学有余力可以研究一下,不做统一要求。
现在你已经知道如何打印n个盘子汉诺塔问题的移动轨迹了,那么在这个的基础上,我们进行以下扩展:
对于n个盘子的汉诺塔问题,给定一个整数m,要求在控制台打印出m + 1步的移动轨迹。
程序的运行图如下所示:
提示:
既然要打印m + 1步的移动轨迹,那么肯定需要计数器记录移动的步骤,需要用什么变量呢?
核心思路就还是move函数的递归,但要加上一个计数器,实际编码时注意边界值就可以了。
参考代码如下:
参考代码如下:
xxxxxxxxxx
53123
4/*
5curr_steps用于指示当前移动的步数
6用static修饰的全局变量可以限制此变量仅在文件内部生效
7为什么不用静态局部变量呢?
8如果在move函数内部使用静态局部变量, 那么就无法将它归1了
9*/
10static int curr_steps;
11
12void move(int n, int m, char start, char target, char sup) {
13// 递归的出口
14if (n == 0) {
15// 没有盘子啥也不做,直接结束递归
16return;
17}
18// 递归体
19// 分解成三步
20// 1.第一步: 将n-1个盘子,从start塔移动到sup塔,target塔用于辅助
21move(n - 1, m, start, sup, target);
22
23// 2.第二步: 将1个盘子,从start塔移动到target塔,这里可以直接打印
24if (curr_steps == m + 1) {
25printf("%c --> %c\n", start, target);
26}
27curr_steps++;
28
29// 3.第三部: 将n-1个盘子,从sup塔移动到target塔,start塔用于辅助
30move(n - 1, m, sup, target, start);
31}
32void next_move_track(int n, int m) {
33if (n <= 0) {
34printf("error: 盘子数量小于等于0.\n");
35return;
36}
37int total_steps = (1LL << n) - 1;
38
39if (m < 0 || m >= total_steps) {
40printf("error: %d个盘子的汉诺塔问题没有%d步.\n", n, (m + 1));
41return;
42}
43curr_steps = 1;
44printf("%d个盘子的汉诺塔问题, 第%d步的移动轨迹是: ", n, (m + 1));
45move(n, m, 'A', 'C', 'B');
46}
47
48int main(void) {
49next_move_track(2, 2);
50next_move_track(3, 2);
51next_move_track(4, 10);
52return 0;
53}
以上。