2024-2025-1 20241407《计算机基础与程序设计》第八周学习总结
作业信息
这个作业属于哪个课程 | 2024-2025-1 计算机基础与程序设计 |
---|---|
这个作业要求在哪里 | 2024-2025-1 计算机基础与程序设计第八周学习总结 |
这个作业的目标 | 功能设计与面向对象设计,面向对象设计过程,面向对象语言三要素,汇编、编译、解释、执行 |
作业正文 | 本博客 |
教材学习内容总结
《计算机科学概论》第七版第九章的主要学习内容如下:
- 面向对象设计(OOD):
- 介绍了面向对象设计的基本概念,包括类和对象。对象是问题背景中相关的事物或实体,而类是一组具有相似属性和行为的对象的描述。
- 面向对象设计过程包括头脑风暴、过滤、场景和责任算法四个阶段。
- 翻译过程:
- 讨论了编译器和解释器的作用。编译器将高级语言编写的程序翻译成机器码,而解释器在输入高级语言编写的程序时,指导计算机执行每个语句指定的动作。
- 程序设计语言范型:
- 探讨了程序设计语言的不同范型,包括命令式范型和声明式范型,它们通过不同方式影响程序员对程序执行的看法。
- 高级程序设计语言:
- 介绍了数据类型,包括整数、实数、字符、布尔型和字符串。
- 讨论了如何将代码组织成函数、层级和对象。
- 面向对象编程:
- 涉及面向对象编程的概念,包括如何将函数打包成对象,以及对象可以包含其他对象、函数和变量。
- API(程序编程接口):
- 讨论了API的作用,即控制哪些函数和数据让外界访问,哪些仅供内部使用。
这些内容为我们提供了计算机科学中面向对象设计和高级程序设计语言的全面概览,包括理论基础和实际应用。
《C语言程序设计》第五版第七章的主要内容涉及模块化程序设计和函数的使用,以下是详细的内容总结:
- 讨论了API的作用,即控制哪些函数和数据让外界访问,哪些仅供内部使用。
- 模块化程序设计:
- 基本思想:模块化程序设计的基本思想是将一个大的程序按功能分割成一些小模块,每个模块相对独立、功能单一、结构清晰、接口简单。
- 优点:模块化程序设计能够控制程序设计的复杂性、提高元件的可靠性、缩短开发周期、避免程序开发的重复劳动,并易于维护和功能扩充。
- 实现方法:把需要共享的函数放在一个单独的.c文件中,把共享函数的函数原型、宏定义和全局变量声明等放在一个单独的.h头文件中。其他需要共享这个函数的程序用#include包含这个头文件后,就可以调用这个函数了。
- 函数的定义和使用:
- 函数分类和定义方法:函数是模块化程序设计的基础,包括函数的分类和定义方法。
- 向函数传递值和从函数返回值:介绍了如何向函数传递值以及如何从函数返回值。
- 函数的嵌套与递归调用:
- 嵌套调用:函数的嵌套调用是指在一个函数内部又调用其他函数。在C语言中,函数的定义是平行的、独立的,即一个函数内部不能包含另一个函数的定义,但函数的调用是允许嵌套的。
- 递归调用:函数的递归调用是指一个函数在执行的过程中直接或间接调用该函数本身。递归函数必须至少有一个基线情况,并且一般情况必须最终能转化为基线情况,否则程序将无限递归下去导致程序出错。
- 变量的作用域与存储类型:
- 变量的作用域:根据变量在程序中定义的位置和方式,变量的作用域可以分为全局作用域和局部作用域。全局变量在整个程序中都可以访问,而局部变量只在定义它的函数或代码块中有效。
- 变量的存储类型:C语言中的变量根据其存储方式可以分为自动变量、静态变量、寄存器变量等。这些存储类型决定了变量的存储位置和生命周期。
这些内容为我们提供了函数编程的基础知识和实践应用,帮助理解和掌握C语言中模块化程序设计的方法。
教材学习中的问题和解决过程
问题一:如何正确地定义和声明一个函数?函数参数和返回值的类型应该如何指定?
解决方式:AI答:在C语言中,正确地定义和声明一个函数涉及到几个关键步骤,包括指定函数的返回类型、函数名、参数列表以及函数体。以下是详细说明:
函数的定义
- 返回类型(Return Type):
- 函数定义的第一部分是返回类型,它指定了函数执行完毕后返回的数据类型。例如,
int
、float
、char
、void
等。
- 函数定义的第一部分是返回类型,它指定了函数执行完毕后返回的数据类型。例如,
- 函数名(Function Name):
- 紧接着返回类型,定义函数的名字。函数名应该是描述性的,以便于理解函数的功能。
- 参数列表(Parameter List):
- 函数名后面的括号内是参数列表,它定义了函数调用时需要传递的参数。参数列表包括参数类型和参数名,多个参数之间用逗号分隔。
- 如果函数不接受任何参数,参数列表应该为空,即
(void)
或者直接()
。
- 函数体(Function Body):
- 参数列表后面的大括号
{}
内是函数体,包含了实现函数功能的代码。
- 参数列表后面的大括号
示例
int add(int a, int b) {
return a + b; // 返回两个整数的和
}
函数的声明
函数声明与定义类似,但不需要函数体。函数声明的目的是告诉编译器函数的接口(返回类型、函数名和参数列表),这样在其他文件中调用该函数时,编译器能够正确地处理函数调用。
示例
// 函数声明
int add(int, int); // 声明一个接受两个int参数并返回int的函数
// 函数定义
int add(int a, int b) {
return a + b;
}
参数和返回值的类型指定
- 参数类型:在参数列表中,每个参数都必须指定一个类型。例如,
int
、float
、double
、char
等。如果函数不接受参数,参数列表应该是(void)
。 - 返回值类型:函数的返回值类型必须在函数定义的最开始指定。如果函数不返回任何值,返回类型应该是
void
。
注意事项
- 函数的返回类型、参数类型和参数名必须正确匹配,否则在编译时会出现错误。
- 函数声明可以在函数定义之前或之后,但在同一个文件或包含该函数声明的头文件中。
- 函数的声明和定义可以位于不同的文件中,通常声明放在头文件中,定义放在源文件中,以实现模块化编程。
通过遵循这些规则,你可以在C语言中正确地定义和声明函数,并指定函数参数和返回值的类型。
问题二:递归函数是如何工作的?怎么应用递归函数?
解决方式:AI答:递归函数是一种在函数定义中调用自身的函数。递归是解决复杂问题的一种强大工具,它允许我们通过将问题分解成更小的、相似的子问题来简化程序的编写。递归函数通常用于处理可以分解为相似子问题的问题,如树和图的遍历、排序算法、数学计算等。
递归函数的工作原理
递归函数的工作原理基于两个主要概念:
- 基本情况(Base Case):
- 基本情况是递归停止的条件。它定义了递归函数直接返回结果而不继续递归的条件。没有基本情况,递归将无限进行下去,最终导致栈溢出错误。
- 递归步骤(Recursive Step):
- 递归步骤是函数调用自身的过程。在这一步,问题被分解为一个或多个更小的子问题,每个子问题都与原始问题相似,但规模更小。
递归函数的一般形式
函数名(参数) {
if (基本情况) {
// 返回直接的结果,不需要进一步递归
} else {
// 递归调用函数本身,参数是经过处理的、更接近基本情况的值
函数名(更小的参数);
}
}
应用递归函数
递归函数的应用非常广泛,以下是一些常见的应用场景:
- 数学计算:
- 计算阶乘:
n! = n * (n-1)!
- 计算斐波那契数列:
F(n) = F(n-1) + F(n-2)
- 计算阶乘:
- 数据结构遍历:
- 二叉树的前序、中序、后序遍历
- 图的深度优先搜索(DFS)
- 排序算法:
- 快速排序:通过递归将数组分为更小的部分并排序
- 字符串和数组处理:
- 字符串反转
- 数组元素的全排列
示例:计算阶乘
#include <stdio.h>
// 函数声明
unsigned long factorial(unsigned int n);
int main() {
unsigned int number;
printf("Enter an unsigned number: ");
scanf("%u", &number);
printf("Factorial of %u = %lu", number, factorial(number));
return 0;
}
// 函数定义
unsigned long factorial(unsigned int n) {
if (n == 0) { // 基本情况
return 1;
} else { // 递归步骤
return n * factorial(n - 1);
}
}
在这个例子中,factorial
函数计算一个数的阶乘。如果输入的数是0,函数返回1(因为0的阶乘定义为1)。否则,函数调用自身来计算(n-1)!
,然后将结果乘以n
。
注意事项
- 避免无限递归:确保递归函数有一个明确的基本情况,以避免无限递归。
- 栈溢出:递归深度过大可能导致栈溢出。在实际应用中,对于深度较大的递归,可能需要考虑使用迭代或其他技术来避免栈溢出。
- 性能:递归函数可能比迭代解决方案更耗时,因为函数调用和返回操作本身需要时间。在性能敏感的应用中,应评估递归和迭代解决方案的效率。
基于AI的学习
上周考试错题总结
错题一:请按如下函数原型编写计算1~n之间的所有素数之和的程序。
int IsPrime(int x)
在主函数中调用该函数计算,并输出1~n之间的所有素数之和
**要求输入提示信息为:无
**要求输入格式为: "%d"
**要求输出格式为:"%d"
错题原因:函数调用过程中实参和形参的设置不准确,且没有在前面声明函数。
总结:以后设置形参和实参时首先应该明确函数的目的和需求然后确定参数的数量和类型,其中数量可以确定函数需要多少个参数来完成其任务,而类型可以确定每个参数的数据类型,确保它们能够支持函数内部的操作。然后是定义形参,需要明确指定每个形参的名称和类型。然后提供合适的实参,在调用函数时,确保提供的实参与形参的类型和数量相匹配。最后还可以测试一下,在还没编写具体函数内容之前先编译运行一下,检验函数的声明和调用有没有错误,然后再继续编写函数,这样能够更加高效地编写代码,并使其错误率减少。
错题二:捕鱼和分鱼。ABCDE五人在某天夜里合伙去捕鱼,到第二天凌晨时都疲惫不堪,于是各自找地方睡觉。日上三竿,A第一个醒来,将鱼分为5份,把多余的1条鱼扔掉,拿走自己的1份。B第二个本来,也将鱼分5份,把多余的1条鱼扔掉,拿走自己的1份。CDE依次醒来,也按同样的方法拿鱼。问他们合伙至少捕了多少条鱼?
**输出格式要求:"Total number of fish catched=%d\n"
错题原因:没有想到定义一个flag并使用嵌套循环,而是选择了穷举法,写了很多表达式导致其中出现了错误,而且效率低下。
总结:在答案给的代码中,flag
的作用是作为一个控制循环的布尔标志位。它的初始值被设置为1,表示循环可以执行。在代码的逻辑中,flag
用来决定是否继续执行外层for
循环。
flag
初始值为1,允许外层for
循环开始执行。
在内层for
循环中,flag
用于检查是否满足特定的条件:
- 如果
(x-1)%5==0
成立,即x-1
是5的倍数,那么x
会被更新为4*(x-1)/5
,flag
保持为1。 - 如果
(x-1)%5
不成立,即x-1
不是5的倍数,那么flag
会被设置为0,表示不再满足继续执行循环的条件。
内层for
循环结束后,会检查flag
的值: - 如果
flag
为1,表示找到了一个满足条件的n
值,外层for
循环通过break
语句终止。 - 如果
flag
为0,外层for
循环的else
部分会将flag
重置为1,然后n
递增,继续下一次外层循环的迭代。
flag
的这种用法允许程序在找到第一个满足特定条件的n
值时停止循环。在这个特定的例子中,条件是n
减去1后能被5整除,并且在内层循环中每次递减为原来的4/5,直到不再满足条件或者i
超过5。
以后要学会运用这种方式,不仅提高了效率,还使代码更加简洁。
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 200/200 | 2/2 | 20/20 | |
第二周 | 200/400 | 1/3 | 18/38 | |
第三周 | 300/700 | 1/4 | 20/58 | |
第四周 | 400/1100 | 1/5 | 22/80 | |
第五周 | 300/1400 | 1/6 | 22/102 | |
第六周 | 400/1800 | 1/7 | 25/127 | |
第七周 | 400/2200 | 1/8 | 30/157 | |
第八周 | 500/2700 | 1/9 | 35/192 |