2024-2025-1 20241413 《计算机基础与程序设计》第十周学习总结
班级链接 | https://edu.cnblogs.com/campus/besti/2024-2025-1-CFAP |
---|---|
作业要求 | https://www.cnblogs.com/rocedu/p/9577842.html#WEEK10 |
作业目标 | 信息系统 数据库与SQL 人工智能与专家系统 人工神经网络 模拟与离散事件 排队系统 天气与地震模型 图形图像 |
教材学习内容总结
《计算机科学概论》第12、13、14章
第12章:组织数据
数据结构:介绍如何组织和存储数据以便于访问和修改。可能包括数组、链表、栈、队列、树和图等。
抽象数据类型(ADT):定义了数据结构的操作和属性,而不需要具体实现细节。
算法分析:讨论算法的时间复杂度和空间复杂度,以及如何评估算法的效率。
第13章:软件工程
软件开发生命周期:介绍软件开发的各个阶段,包括需求分析、设计、编码、测试和维护。
软件设计原则:包括模块化、抽象、封装和信息隐藏等原则。
软件测试:讨论不同类型的测试方法,如单元测试、集成测试、系统测试和验收测试。
项目管理:涉及项目规划、风险管理、团队协作和进度跟踪。
第14章:算法
算法基础:回顾算法的定义、特性和重要性。
排序和搜索算法:介绍基本的排序算法(如冒泡排序、选择排序、插入排序)和搜索算法(如线性搜索、二分搜索)。
算法效率:讨论如何通过大O符号来分析算法的时间复杂度。
算法设计技术:介绍递归、分治、动态规划和贪心算法等算法设计技术。*
《C语言程序设计》第9章
函数的进一步讨论:
函数原型:介绍如何在函数定义之前声明函数原型。
函数指针:如何声明和使用指向函数的指针。
递归:如何编写和理解递归函数。
指针的高级应用:
指针和数组:深入探讨指针与数组之间的关系,包括指针算术和数组作为参数传递给函数。
指针与动态内存分配:介绍如何使用malloc、calloc、realloc和free等函数在堆上动态分配和释放内存。
结构体(struct):
结构体定义:如何定义和使用结构体来创建复杂的数据类型。
结构体数组:如何创建和操作结构体数组。
结构体与函数:如何将结构体作为参数传递给函数,以及如何从函数返回结构体。
联合(union)和枚举(enum):
联合:介绍如何使用联合来存储不同的数据类型,但在同一时间只能使用其中一个。
枚举:介绍如何定义和使用枚举类型,它提供了一种方式来为一组整数值赋予更易读的名字。
位运算:
位运算符:介绍位运算符(如&、|、^、~、<<、>>)及其在C语言中的应用。
位域:如何使用位域来紧凑地存储数据。
预处理器指令:
宏定义:如何使用#define来创建宏。
文件包含:介绍#include指令及其作用。
条件编译:如何使用#ifdef、#ifndef、#endif等指令进行条件编译。
文件操作:
文件指针:如何使用文件指针进行文件的打开、关闭、读取和写入操作。
格式化输入输出:使用fprintf、fscanf、fopen、fclose等函数进行文件的格式化输入输出。
错误处理:
错误检测:如何检测和处理运行时错误,例如内存分配失败或文件操作错误。
教材学习中的问题与解决过程
1.当嵌套函数中的两个值都需要返回至主函数中时怎么办?
。
使用结构体:
定义一个包含多个成员的结构体,然后让函数返回这个结构体。这样可以获得多个计算结果,代码也更清晰:
c
struct Result {
int sum;
int product;
float average;
};
struct Result calculate(int a, int b) {
struct Result res;
res.sum = a + b;
res.product = a * b;
res.average = (float)(a + b) / 2;
return res;
}
int main() {
int x = 5, y = 3;
struct Result result = calculate(x, y);
printf("Sum: %d\n", result.sum);
printf("Product: %d\n", result.product);
printf("Average: %.2f\n", result.average);
return 0;
}
这种方法的优点是代码更加清晰,而且可以返回不同类型的数据
。
使用数组:
将多个结果存储在一个数组中,然后返回这个数组或修改作为参数传入的数组:
c
void calculate(int a, int b, int results[]) {
results[0] = a + b;
results[1] = a * b;
results[2] = a - b;
}
int main() {
int x = 5, y = 3;
int results[3];
calculate(x, y, results);
printf("Sum: %d\n", results[0]);
printf("Product: %d\n", results[1]);
printf("Difference: %d\n", results[2]);
return 0;
}
这种方法的优点是可以返回任意数量的值,缺点是需要预先知道返回值的数量和类型 。
2.指针函数需要返回值吗?
指针函数通常指的是返回指针类型的函数,而不是“函数指针”。对于返回指针类型的函数,它们并不“需要”返回值,就像任何其他类型的函数一样,可以选择返回值,也可以不返回值。但是,通常我们定义返回指针类型的函数是为了通过这个指针返回某些数据,因为指针可以指向一块内存,从而允许函数返回一个复杂的数据结构或者动态分配的内存。
如果一个函数声明了返回指针类型,那么在函数的实现中,通常需要有一个返回语句来返回一个有效的指针。如果没有返回值,或者返回了一个无效的指针,那么调用这个函数的代码可能会遇到未定义行为,比如访问非法内存区域,导致程序崩溃。
例如,以下是一个返回指针的函数:
c
int* createArray(int size) {
int* array = (int*)malloc(size * sizeof(int));
if (array == NULL) {
// 处理内存分配失败的情况
return NULL;
}
// 初始化数组...
return array; // 返回指向数组的指针
}
在这个例子中,createArray 函数返回一个指向动态分配的整型数组的指针。如果内存分配成功,它返回这个新分配的数组的地址;如果分配失败,它返回 NULL。
如果一个返回指针类型的函数不返回任何值,或者返回了一个未初始化的指针,那么调用这个函数的代码可能会试图解引用一个无效的指针,这通常会导致程序崩溃或者产生其他未定义的行为。
因此,虽然指针函数不“需要”返回值,但在实际编程中,我们通常期望它们返回一个有效的指针,以便能够通过这个指针来访问或操作数据。
基于AI的学习
User: 我在学习《c语言程序设计》第九章指针的内容,请你以苏格拉底的方式,一次向我提问一个问题
Kimi: 当然可以,让我们开始这个探索之旅。首先,我想问问你:
你能告诉我什么是指针吗?
User: 变量内容为地址的数据类型
Kimi: 非常好,你已经掌握了指针的基本定义。那么,我再问你一个问题:
指针变量可以存储哪些类型的地址?
User: 所有类型
Kimi: 接近了,但需要更具体一些。在C语言中,指针可以指向任何类型的数据,但每种类型的指针只能指向与其类型相匹配的数据。那么,我的下一个问题:
你能举例说明如何声明一个指向整型数据的指针吗?
User: int *a=&b;
Kimi: 你提供了一个声明整型指针并初始化它的例子,这是正确的。这里 int *a
声明了一个指向整型的指针 a
,而 &b
表示取变量 b
的地址并将其赋值给指针 a
。这里假设 b
是一个已经声明的整型变量。
那么,我的下一个问题:
如果 a
是一个指向整型的指针,那么如何通过 a
来访问它所指向的整型变量的值?
User: *a即使
Kimi: 你的回答是正确的,*a
确实是用来访问指针 a
指向的值。在C语言中,*
符号有两种用途:一种是作为指针的声明,另一种是作为解引用操作符。在你的例子中,*a
作为解引用操作符,用来获取指针 a
指向的内存地址中存储的值。这样,即使我们不知道变量 b
的具体值,我们也可以间接地通过 *a
来访问和修改它。
User: 继续提问
Kimi: 很好,你已经理解了如何使用指针来访问和修改变量的值。那么,让我们进一步探讨:
如果有两个指针变量,它们指向同一个整型变量,那么通过其中一个指针修改了这个变量的值,另一个指针会受到影响吗?为什么?
User: 会,因为这个实参已经被改变了
Kimi: 是的,你的回答是正确的。如果两个指针变量指向同一个整型变量,那么它们共享相同的内存地址。因此,通过任何一个指针对变量的修改都会反映到另一个指针上,因为它们实际上指向的是同一块内存空间。
接下来,我有一个新问题:
在C语言中,指针可以指向指针吗?如果可以,这样的指针被称为什么?
User: 可以
Kimi: 确实,指针可以指向指针,这样的指针被称为“指向指针的指针”或“双重指针”。这种指针通常用于多级指针操作,比如动态分配二维数组。
那么,我的下一个问题:
在C语言中,双重指针如何声明?你能提供一个例子吗?
User: 你的回答呢
Kimi: 当然,我来提供一个双重指针的声明和使用的例子。
在C语言中,声明一个指向指针的指针(即双重指针)时,你需要在指针变量名前放置两个星号(**
)。这里是一个声明和初始化双重指针的例子:
int a = 10; // 声明一个整型变量
int *p = &a; // 声明一个指向整型的指针,并指向a
int **q = &p; // 声明一个指向指向整型指针的指针(双重指针),并指向p
在这个例子中,a
是一个整型变量,p
是一个指向整型的指针,它存储了 a
的地址。q
是一个双重指针,它存储了 p
的地址。通过 q
,我们可以间接地访问 p
,进而访问 a
的值。
现在,让我们继续提问:
如果有一个双重指针 int **q
,并且你想要通过 q
来修改它指向的指针所指向的整型变量的值,你应该怎么做?
User: 给**p重新赋予p的值
Kimi: 实际上,如果你想要通过双重指针 q
来修改它指向的指针所指向的整型变量的值,你不需要重新给 **q
赋值,而是直接通过解引用操作来修改值。这里是一个如何操作的例子:
**q = 20; // 通过双重指针q修改它指向的指针p所指向的变量a的值
这行代码将 a
的值从原来的 10 改为了 20。这里的 **q
首先解引用 q
得到 p
,然后再解引用 p
来修改 a
的值。
那么,我的下一个问题:
在C语言中,指针可以指向函数吗?如果可以,声明一个指向函数的指针需要注意什么?
User: 可以
Kimi: 很好,你已经确认了指针可以指向函数。那么,让我们深入探讨一下如何声明一个指向函数的指针。在C语言中,声明一个指向函数的指针需要遵循特定的格式,这个格式包括函数的返回类型、指针变量名以及参数列表。这里有一个关键点需要注意:
声明指向函数的指针时,必须确保指针的类型与它所指向的函数的返回值类型和参数列表完全匹配。 这意味着,如果你有一个返回整型值并且接受两个整型参数的函数,你应该这样声明一个指向该函数的指针:
int (*pointerName)(int, int);
在这个声明中,int
是函数的返回值类型,pointerName
是你为指针变量选择的名字,而括号内的 (int, int)
是函数的参数列表,表明这个函数接受两个整型参数。
此外,当你使用函数指针时,你可以直接将函数名赋给指针变量,因为函数名在大多数情况下会被编译器视为指向该函数的指针。例如:
int (*pointerName)(int, int) = functionName;
在这里,functionName
是你想要指向的函数的名称,而 pointerName
是你的函数指针变量。
请记住,函数指针的使用需要严格遵守类型匹配的原则,否则可能会导致编译错误或者运行时错误。希望这能帮助你更好地理解如何在C语言中声明和使用指向函数的指针。如果你有任何疑问或需要进一步的解释,请随时提问。