2024-2025-1 20241308《计算机基础与程序设计》第十三周学习总结
作业信息
这个作业属于哪个课程 <班级的链接>2024-2025-1-计算机基础与程序设计)
这个作业要求在哪里 <作业要求的链接>https://www.cnblogs.com/rocedu/p/9577842.html#WEEK13
这个作业的目标 第12章并完成云班课测试
作业正文
教材学习内容总结
一、从基本数据类型到抽象数据类型
- C语言基本数据类型回顾
在C语言中,基本数据类型包括整型(int、short、long等)、浮点型(float、double)、字符型(char)和布尔型(在C99标准引入_Bool,头文件stdbool.h中定义了bool为_Bool的别名,true和false为宏)。
例如,定义一个整型变量int num = 10;
,这里num存储一个整数值。使用基本数据类型可以进行简单的运算和数据存储。
基本数据类型的操作相对简单直接,如对于整型可以进行算术运算(+
、-
、*
、/
),对于字符型可以进行字符的比较、输出等操作。 - 抽象数据类型在C语言中的体现 - 以链表为例
定义链表节点的结构体(数据结构)
链表是一种常见的抽象数据类型,在C语言中可以使用结构体来构建链表节点。
例如,定义一个简单的整数链表节点结构体如下:
c
// 定义链表节点结构体
typedef struct ListNode {
int data;
struct ListNode* next;
} ListNode;
这里的ListNode
结构体包含两个成员,一个是int类型的数据data,用于存储节点的数据;另一个是指向ListNode类型的指针next,用于指向下一个节点,通过这种方式可以将多个节点连接起来形成链表。
链表操作的函数定义(抽象操作)
创建节点函数
可以定义一个函数来创建一个链表节点。
例如:
c
// 创建节点函数
ListNode* createNode(int value) {
ListNode* newNode = (ListNode)malloc(sizeof(ListNode));
if (newNode == NULL) {
// 内存分配失败处理
return NULL;
}
newNode->data = value;
newNode->next = NULL;
return newNode;
}
这个函数接受一个整数value,通过动态内存分配(malloc)创建一个新的链表节点,将value
赋值给节点的数据成员data
,并将next
指针初始化为NULL
,最后返回新创建的节点指针。
插入节点函数
- 定义一个函数来将一个节点插入到链表中合适的位置。
- 例如,将一个节点插入到链表头部的函数如下:
c
// 插入节点到头部函数
void insertAtHead(ListNode** head, int value) {
ListNode newNode = createNode(value);
newNode->next = head;
head = newNode;
}
这个函数接受一个指向链表头节点指针的指针head
和一个整数value
。首先调用createNode
函数创建一个新节点,然后将新节点的next
指针指向当前的头节点,最后将新节点赋值给*head
,从而实现将节点插入到链表头部。
遍历链表函数
可以定义一个函数来遍历链表并输出节点数据。
例如:
c
// 遍历链表函数
void traverseList(ListNode head) {
ListNode current = head;
while (current!= NULL) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
}
这个函数接受一个链表头节点指针head
,通过一个循环,从链表头开始,每次输出当前节点的数据(current->data
),然后将当前节点指针移动到下一个节点(current = current->next
),直到当前节点为NULL
,表示链表遍历结束。 - 从基本数据类型到抽象数据类型的过渡意义
模块化和代码复用:通过将链表相关的操作封装在函数中,使得代码更加模块化。在其他程序中,如果需要使用链表,只需要包含这些函数定义和结构体定义,就可以方便地使用链表功能,提高了代码的复用性。
数据抽象和隐藏实现细节:对于使用链表的程序员来说,不需要了解链表内部节点是如何在内存中分配和连接的具体细节(如malloc
的使用等)。他们只需要知道如何调用插入、遍历等操作函数来处理链表数据,实现了数据抽象和隐藏细节的功能。这使得程序的层次结构更加清晰,易于理解和维护。
二、结构体的定义
在C语言中,结构体是一种用户自定义的数据类型,用于将不同类型的数据组合在一起。
以下是结构体定义的一般形式:
struct 结构体名 {
数据类型 成员1;
数据类型 成员2;
//...
数据类型 成员n;
};
例如,定义一个包含学生信息(姓名、年龄、成绩)的结构体:
struct Student {
char name[20];
int age;
float score;
};
在这个例子中, struct Student 是结构体类型名, name 、 age 和 score 是结构体的成员。 name 是一个字符数组,用来存储学生姓名; age 是一个整数,用于存储学生年龄; score 是一个浮点数,用于存储学生成绩。
定义结构体变量有多种方式。可以在定义结构体类型之后定义变量,比如:
struct Student stu1;
这里 stu1 就是 struct Student 类型的变量,可以通过 stu1.name 、 stu1.age 和 stu1.score 来访问和操作结构体变量中的成员。
也可以在定义结构体类型的同时定义变量,例如:
struct Student {
char name[20];
int age;
float score;
} stu2;
这里 stu2 也是 struct Student 类型的变量。
三、结构体数组的定义和初始化
在C语言中,结构体数组是一个包含多个结构体元素的数组。
定义
- 先定义结构体类型,再定义结构体数组。例如,定义一个存储学生信息的结构体数组:
struct Student {
char name[20];
int age;
float score;
};
struct Student students[3];
这里定义了一个 students 数组,它有3个元素,每个元素都是 struct Student 类型。 - 也可以在定义结构体类型的同时定义结构体数组:
struct Student {
char name[20];
int age;
float score;
} students[3];
初始化 - 按顺序初始化。在定义结构体数组时可以对其进行初始化,例如:
struct Student students[3] = {
{"Alice", 18, 90.5},
{"Bob", 19, 85.0},
{"Charlie", 20, 92.0}
};
这里为 students 数组的3个元素分别初始化了成员的值,每个花括号内的值对应一个结构体元素的成员。 - 部分初始化。如果只想初始化部分元素,例如只初始化第一个元素:
struct Student students[3] = {{"Alice", 18, 90.5}};
其余元素的成员将被默认初始化为0(对于数值类型)或空字符(对于字符数组)。
四、结构体指针的定义和初始化
在C语言中,结构体指针是指向结构体的指针变量。
定义
- 先定义结构体类型,再定义结构体指针。例如:
struct Student {
char name[20];
int age;
float score;
};
struct Student *p;
这里 p 是一个指向 struct Student 类型结构体的指针。
初始化 - 可以在定义指针时初始化。如果已经有一个结构体变量 stu ,可以将指针指向这个变量:
struct Student stu;
struct Student *p = &stu;
这样, p 就指向了 stu 结构体变量,通过 p 可以访问 stu 的成员。访问方式主要有两种: - 使用 (p).成员名 ,例如 (p).age 来访问 stu 的年龄成员。不过这种方式比较繁琐。
- 更常用的是使用 p->成员名 ,如 p->age 同样可以访问 stu 的年龄成员
五、向函数传递结构体
在C语言中,有三种方式可以向函数传递结构体。
传递结构体变量
- 定义:将整个结构体变量作为参数传递给函数。
- 示例:
include <stdio.h>
// 定义结构体
struct Point {
int x;
int y;
};
// 函数声明,参数为结构体变量
void printPoint(struct Point p) {
printf("x = %d, y = %d\n", p.x, p.y);
}
int main() {
struct Point point = {1, 2};
printPoint(point);
return 0;
}
- 说明:在函数调用时,会创建一个新的结构体副本,函数内对结构体的操作不会影响原始结构体变量。这可能会占用额外的内存和时间,尤其是结构体较大时。
传递结构体指针 - 定义:将指向结构体的指针传递给函数。
- 示例:
include <stdio.h>
struct Point {
int x;
int y;
};
// 函数声明,参数为结构体指针
void printPoint(struct Point *p) {
printf("x = %d, y = %d\n", p->x, p->y);
}
int main() {
struct Point point = {1, 2};
printPoint(&point);
return 0;
}
- 说明:这种方式更高效,因为只传递了结构体的地址,而不是整个结构体的副本。并且函数可以通过指针修改原始结构体的内容。
传递结构体数组 - 定义:将结构体数组作为参数传递给函数。
- 示例:
include <stdio.h>
struct Point {
int x;
int y;
} points[2];
// 函数声明,参数为结构体数组
void printPoints(struct Point arr[], int n) {
for (int i = 0; i < n; i++) {
printf("x = %d, y = %d\n", arr[i].x, arr[i].y);
}
}
int main() {
points[0].x = 1;
points[0].y = 2;
points[1].x = 3;
points[1].y = 4;
printPoints(points, 2);
return 0;
}
- 说明:在函数参数中, struct Point arr[] 和 struct Point *arr 是等价的,都表示传递一个指向结构体的指针,函数可以通过这个指针访问数组中的每个结构体元素。
六、共用体
在C语言中,共用体(也叫联合体)是一种特殊的数据类型。
定义
- 共用体的定义形式与结构体类似。例如:
union Data {
int i;
float f;
char str[20];
};
这里定义了一个名为 union Data 的共用体,它包含一个整数 i 、一个浮点数 f 和一个字符数组 str 。
特点 - 共用体的所有成员共用同一块内存空间。例如在 union Data 中, i 、 f 和 str 在内存中的起始地址是相同的。
- 共用体的大小取决于其最大成员的大小。在上述例子中,如果 int 占4字节、 float 占4字节、 char str[20] 占20字节,那么 union Data 的大小就是20字节。
初始化与使用 - 初始化:可以在定义共用体变量时进行初始化,不过只能初始化其中一个成员。例如:
union Data data = {.i = 10}; - 使用:同一时刻只能使用共用体中的一个成员,因为它们共用内存。例如:
printf("%d", data.i);
这就访问了共用体中的整数成员 i 。如果之后访问 data.f 或者 data.str ,就会按照 float 或者 char 类型来解释这块内存区域。
共用体常用于节省内存,当需要在不同时间使用不同类型的数据来占用同一块内存空间时很有用。
七、枚举数据类型
在C语言中,枚举(enumeration)是一种用户自定义的数据类型。
定义
- 基本形式是使用 enum 关键字来定义。例如,定义一个表示星期几的枚举类型:
enum Weekday {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
};
这里 enum Weekday 是一个枚举类型,花括号内的 MONDAY 、 TUESDAY 等是枚举常量,它们默认从0开始依次递增,所以 MONDAY 的值是0, TUESDAY 的值是1,以此类推。
自定义枚举常量的值 - 可以在定义时为枚举常量指定值。例如:
enum Weekday {
MONDAY = 1,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
};
此时 MONDAY 的值是1, TUESDAY 的值是2,后面的枚举常量在前面值的基础上依次递增。
使用枚举类型 - 可以定义枚举变量。例如:
enum Weekday today;
today = WEDNESDAY; - 枚举类型在 switch 语句中很有用。例如:
switch (today) {
case MONDAY:
printf("星期一,开始新的一周工作。");
break;
case TUESDAY:
printf("星期二,继续加油。");
break;
// 其他case语句
default:
printf("不是工作日。");
}
枚举类型增强了程序的可读性,通过有意义的枚举常量名字来表示一组相关的整数值,使代码更清晰,便于理解和维护。
八、动态数据结构——单向链表
1. 概念
- 单向链表是一种常见的动态数据结构。它由一系列节点组成,每个节点包含数据部分和指向下一个节点的指针。节点之间通过指针相连,形成一个线性序列。
2. 节点结构定义(以C语言为例) - typedef struct Node {
int data; // 数据部分,可以是任何类型,这里以整数为例
struct Node *next; // 指向下一个节点的指针
} Node; - 这里 typedef 关键字用于给 struct Node 这个结构体类型起一个别名 Node ,方便后续使用。
3. 链表的基本操作 - 创建节点:
- Node *createNode(int value) {
Node *newNode = (Node *)malloc(sizeof(Node));
if (newNode == NULL) {
return NULL;
}
newNode->data = value;
newNode->next = NULL;
return newNode;
} - 这个函数用于创建一个新的节点。首先使用 malloc 函数分配内存空间来存储节点, sizeof(Node) 计算节点所需的内存大小。如果 malloc 返回 NULL ,说明内存分配失败,就返回 NULL 。然后初始化节点的数据部分和指针部分,将指针设为 NULL ,表示该节点暂时没有下一个节点。
- 插入节点(头部插入):
- Node *insertAtHead(Node *head, int value) {
Node *newNode = createNode(value);
if (newNode == NULL) {
return head;
}
newNode->next = head;
return newNode;
} - 这个函数用于在链表头部插入一个新节点。首先创建一个新节点,然后将新节点的 next 指针指向当前的头节点 head ,最后返回新节点作为新的头节点。
- 遍历链表:
- void traverseList(Node *head) {
Node *current = head;
while (current!= NULL) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
} - 这个函数从链表的头节点开始,通过 current 指针逐个访问节点的数据部分。当 current 指针为 NULL 时,说明已经到达链表的末尾,遍历结束。
- 查找节点:
- Node *findNode(Node *head, int value) {
Node *current = head;
while (current!= NULL) {
if (current->data == value) {
return current;
}
current = current->next;
}
return NULL;
} - 该函数在链表中查找数据值为 value 的节点。从头节点开始,逐个比较节点的数据部分。如果找到匹配的节点,就返回该节点的指针;如果遍历完整个链表都没有找到,就返回 NULL 。