学期:2024-2025-1 学号:20241303 《计算机基础与程序设计》第十三周学习总结
作业信息
这个作业属于哪个课程 | <班级的链接>(如2024-2025-1-计算机基础与程序设计) |
---|---|
这个作业要求在哪里 | <作业要求的链接>(如2024-2025-1计算机基础与程序设计第十三周作业) |
这个作业的目标 | <写上具体方面>加入云班课,参考本周学习资源;自学教材《C语言程序设计》第12章并完成云班课测试 |
作业正文 | ... 本博客链接 |
教材学习内容总结
结构体
一、结构体的定义
- 基本定义形式
- 结构体是一种用户自定义的数据类型,它允许将不同类型的数据组合在一起。定义结构体的基本语法如下:
struct结构体名{ 数据类型 成员1; 数据类型 成员2; //... };
- 例如,定义一个表示学生信息的结构体:
struct Student { char name[20]; int age; float score; };
- 这里定义了一个名为
Student
的结构体,它包含了一个字符数组name
(用于存储学生姓名)、一个整型变量age
(用于存储学生年龄)和一个单精度浮点型变量score
(用于存储学生成绩)。
- 使用
typedef
定义结构体别名- 为了方便使用结构体类型,可以使用
typedef
关键字为结构体类型定义一个别名。语法如下:
typedef struct结构体名{ 数据类型 成员1; 数据类型 成员2; //... }结构体别名;
- 例如,对于上面的
Student
结构体,可以这样定义别名:
typedef struct Student { char name[20]; int age; float score; }Stu;
- 之后就可以使用
Stu
来声明结构体变量,如Stu stu1;
,这样代码更加简洁。
- 为了方便使用结构体类型,可以使用
二、结构体变量的声明和初始化
- 声明结构体变量
- 在定义了结构体类型后,可以声明结构体变量。有以下两种常见方式:
- 方式一:先定义结构体类型,再声明变量。
struct Student { char name[20]; int age; float score; }; struct Student stu1;
- 方式二:在定义结构体类型的同时声明变量。
struct Student { char name[20]; int age; float score; } stu1;
- 初始化结构体变量
- 可以在声明结构体变量时对其进行初始化。
- 例如,初始化一个
Student
结构体变量:
struct Student stu1 = {"Tom", 18, 90.5};
- 按照结构体成员定义的顺序,依次为每个成员赋初始值。如果只想初始化部分成员,可以这样做:
struct Student stu2 = {.age = 19};
- 这里只初始化了
age
成员,其他成员的值是未定义的(如果是全局变量,会被初始化为0;如果是局部变量,是随机值)。
三、结构体成员的访问
- 通过点运算符(.)访问成员
- 对于普通的结构体变量,可以使用点运算符来访问结构体中的成员。
- 例如,对于前面定义的
stu1
变量,如果要访问其姓名成员并输出,可以这样写:
printf("Name: %s\n", stu1.name);
- 如果要修改
stu1
的年龄成员,可以这样写:
stu1.age = 20;
- 通过指针访问成员(->运算符)
- 当有一个指向结构体的指针时,使用
->
运算符来访问结构体成员。 - 例如,有如下代码:
struct Student *pstu = &stu1;
- 要访问
stu1
的成绩成员,可以这样写:
printf("Score: %f\n", pstu->score);
- 当有一个指向结构体的指针时,使用
四、结构体数组
- 定义和初始化结构体数组
- 结构体数组是一个数组,其元素是结构体类型。定义结构体数组的语法如下:
struct结构体名 数组名[数组大小];
- 例如,定义一个包含3个
Student
结构体的数组:
struct Student class[3];
- 初始化结构体数组可以这样写:
struct Student class[3] = {{"Alice", 17, 88.0}, {"Bob", 18, 92.0}, {"Cindy", 19, 85.0}};
- 访问结构体数组中的元素和成员
- 可以通过数组下标来访问结构体数组中的元素,然后再使用点运算符或
->
运算符访问元素中的成员。 - 例如,要输出第二个学生的姓名,可以这样写:
printf("Second student's name: %s\n", class[1].name);
- 可以通过数组下标来访问结构体数组中的元素,然后再使用点运算符或
五、结构体的嵌套
- 定义嵌套结构体
- 结构体中可以包含另一个结构体作为其成员。例如,定义一个包含日期结构体的学生结构体:
struct Date { int year; int month; int day; }; struct StudentWithDate { char name[20]; struct Date birthday; int age; };
- 访问嵌套结构体中的成员
- 访问嵌套结构体中的成员时,需要使用多个点运算符。例如,对于
struct StudentWithDate
类型的变量stu
,要访问其生日的年份,可以这样写:
struct StudentWithDate stu; printf("Birth year: %d\n", stu.birthday.year);
- 访问嵌套结构体中的成员时,需要使用多个点运算符。例如,对于
共用体
一、共用体的定义
- 共用体(也称为联合体)是一种特殊的数据类型,它允许在同一段内存存储不同类型的数据,但在某一时刻,这段内存空间只能存储其中一种类型的数据。其定义语法如下:
union 共用体名 {
数据类型 成员1;
数据类型 成员2;
//...
};
- 例如,定义一个名为
Data
的共用体:
union Data {
int i;
float f;
char str[20];
};
- 这个共用体
Data
有三个成员,分别是整型i
、单精度浮点型f
和字符数组str
。它们共享同一段内存空间。
二、共用体变量的声明和初始化
- 声明共用体变量
- 与结构体类似,声明共用体变量有两种方式。
- 方式一:先定义共用体类型,再声明变量。
union Data { int i; float f; char str[20]; }; union Data data;
- 方式二:在定义共用体类型的同时声明变量。
union Data { int i; float f; char str[20]; } data;
- 初始化共用体变量
- 可以在声明共用体变量时对其进行初始化。不过要注意,由于共用体成员共享内存,初始化时只能初始化一个成员。
- 例如:
union Data data = {.i = 10};
- 这里初始化了共用体
Data
的整型成员i
为10。
三、共用体成员的访问
- 访问共用体成员的方式和结构体类似,使用点运算符(.)。
- 例如,对于上面声明的
data
变量,如果要访问其整型成员i
并输出,可以这样写:
printf("The value of i: %d\n", data.i);
- 如果要修改共用体成员的值,例如将
i
的值修改为20,可以这样写:
data.i = 20;
四、共用体的大小
- 共用体的大小取决于其最大成员的大小。这是因为所有成员共享同一块内存空间,为了能够容纳最大的成员,共用体的大小至少要等于最大成员的大小。
- 例如,在前面定义的
union Data
中,str
成员是最大的(假设在这个环境中,一个int
占4字节,一个float
占4字节,str
数组占20字节),所以union Data
的大小通常是20字节。不过,在实际的编译器实现中,可能会因为字节对齐等因素而有所不同。字节对齐是为了提高内存访问效率,编译器可能会在成员之间插入一些填充字节。
五、共用体与结构体的区别
- 内存存储方式
- 结构体的每个成员都有自己独立的内存空间,结构体变量的大小是所有成员大小之和(考虑字节对齐)。例如,一个结构体中有一个
int
(4字节)和一个float
(4字节),它的大小至少是8字节。 - 共用体的所有成员共享同一段内存空间,其大小取决于最大成员的大小。
- 结构体的每个成员都有自己独立的内存空间,结构体变量的大小是所有成员大小之和(考虑字节对齐)。例如,一个结构体中有一个
- 数据使用特点
- 结构体可以同时存储和访问多个不同类型的数据成员。例如,可以同时获取一个学生结构体中的姓名、年龄和成绩等多个信息。
- 共用体在某一时刻只能存储和使用其中一个成员的数据。例如,对于前面的
union Data
,当存储了一个整型数据后,再存储一个浮点型数据,原来的整型数据就会被覆盖。
六、共用体的应用场景
- 节省内存空间
- 在一些资源受限的嵌入式系统或对内存要求苛刻的应用中,当多个数据成员不会同时使用时,使用共用体可以节省内存。例如,在一个通信协议的数据包解析程序中,数据包可能有多种不同类型的负载(如整型数据、浮点型数据或者字符数组形式的数据),但每次只会处理其中一种类型的负载,这时可以使用共用体来表示数据包的负载部分。
- 实现数据类型的转换
- 可以利用共用体来实现数据类型的转换。例如,将一个
int
类型的数据和一个float
类型的数据进行位模式的转换。通过将它们放在一个共用体中,先给一个成员赋值,然后通过访问另一个成员来获取转换后的数据。不过这种方式要注意字节顺序等问题,因为不同的计算机体系结构可能有不同的字节顺序。
- 可以利用共用体来实现数据类型的转换。例如,将一个
单项列表
- 定义
- 在C语言中,单项列表(也称为单链表)是一种常见的数据结构。它由一系列节点组成,每个节点包含数据部分和指向下一个节点的指针。节点的结构通常可以定义如下:
typedef struct Node { int data; // 数据部分,可以是任何类型,这里以int为例 struct Node* next; // 指向下一个节点的指针 } Node;
- 这里使用
typedef
为struct Node
结构体定义了别名Node
,方便后续使用。每个节点的数据部分存储实际的数据(这里是int
类型的数据),next
指针用于连接下一个节点,从而形成链表结构。
- 创建节点
- 要创建一个节点,可以使用
malloc
函数动态分配内存。例如:
Node* createNode(int value) { Node* newNode = (Node*)malloc(sizeof(Node)); if (newNode == NULL) { // 内存分配失败 return NULL; } newNode->data = value; newNode->next = NULL; return newNode; }
- 这个函数接受一个
int
值作为参数,用于初始化节点的数据部分。首先通过malloc
分配足够的内存来存储Node
结构体,如果分配成功,将传入的值赋给节点的数据部分,并且将next
指针初始化为NULL
,表示这个节点目前是链表的最后一个节点,最后返回新创建的节点指针。
- 要创建一个节点,可以使用
- 构建单链表
- 可以通过逐个添加节点来构建单链表。例如,下面的函数可以将一个新节点添加到链表的头部:
Node* insertAtHead(Node* head, int value) { Node* newNode = createNode(value); if (newNode == NULL) { return head; } newNode->next = head; return newNode; }
- 这个函数首先调用
createNode
函数创建一个新节点。如果创建成功,将新节点的next
指针指向当前的头节点head
,然后将新节点作为新的头节点返回。这样就实现了在链表头部插入节点的操作。
- 遍历单链表
- 遍历单链表可以通过从头节点开始,沿着
next
指针逐个访问节点,直到遇到NULL
指针为止。例如:
void traverseList(Node* head) { Node* current = head; while (current!= NULL) { printf("%d ", current->data); current = current->next; } printf("\n"); }
- 这个函数接受一个头节点指针
head
,首先将一个临时指针current
指向头节点。然后在while
循环中,只要current
指针不为NULL
,就输出当前节点的数据部分,并将current
指针移动到下一个节点。当current
指针为NULL
时,说明已经遍历完整个链表,最后输出一个换行符。
- 遍历单链表可以通过从头节点开始,沿着
- 查找节点
- 可以在单链表中查找特定数据的节点。例如,下面的函数用于在链表中查找数据为
target
的节点:
Node* searchNode(Node* head, int target) { Node* current = head; while (current!= NULL) { if (current->data == target) { return current; } current = current->next; } return NULL; }
- 这个函数和遍历链表的过程类似,在遍历过程中,当遇到节点的数据部分等于
target
时,就返回该节点的指针。如果遍历完整个链表都没有找到符合条件的节点,就返回NULL
。
- 可以在单链表中查找特定数据的节点。例如,下面的函数用于在链表中查找数据为
- 删除节点
- 删除单链表中的节点有多种情况。例如,删除头节点可以这样做:
Node* deleteAtHead(Node* head) { if (head == NULL) { return NULL; } Node* temp = head; head = head->next; free(temp); return head; }
-
这个函数首先判断链表是否为空,如果为空则直接返回
NULL
。如果不为空,将头节点保存在一个临时指针temp
中,然后将头节点更新为原来头节点的下一个节点,最后释放原来头节点所占用的内存并返回新的头节点。 -
删除链表中除头节点外的其他节点稍微复杂一些。例如,要删除数据为
value
的节点(假设链表中存在该节点且不是头节点):
Node* deleteNode(Node* head, int value) { if (head == NULL) { return NULL; } Node* current = head; Node* prev = NULL; while (current!= NULL && current->data!= value) { prev = current; current = current->next; } if (current == NULL) { // 未找到要删除的节点 return head; } if (prev == NULL) { // 要删除的节点是头节点 head = head->next; } else { prev->next = current->next; } free(current); return head; }
- 这个函数使用两个指针
current
和prev
,current
用于遍历链表查找要删除的节点,prev
用于记录current
的前一个节点。在找到要删除的节点后,如果prev
为NULL
,说明要删除的是头节点,直接更新头节点;否则,将prev
的next
指针指向current
的下一个节点,从而将current
节点从链表中删除,最后释放current
节点占用的内存并返回更新后的头节点。
基于AI的学习
代码调试中的问题和解决过程
问题1:
输出倒三角形图案
Description
输入正整数n,输出n行由*构成的倒置等腰三角形填充图案。
Input
输入正整数n,其中n大于1且不超过30,输入格式为:"%d"。
Output
输出n行由*构成的倒置等腰三角形填充图案。
方法:
#include<stdio.h>
int main()
{
int n,i,j;
char a='*';
char b=' ';
scanf("%d",&n);
for(i=n; i>0; i--)
{
for(j=i+1;j<=n;j++)
{
printf("%c",b);
}
for(j=2*i-1; j>0; j--)
{
printf("%c",a);
}
printf("\n");
}
return 0;
}
其他(感悟、思考等,可选)
复习
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第十二周 | 2100/3000 | 1/2 | 20/20 | |
第十三周 | 2300/3000 | 1/4 | 18/38 | |
第十四周 | 500/1000 | 3/7 | 22/60 | |
第十五周 | 300/1300 | 2/9 | 30/90 |