作业信息
这个作业属于哪个课程 | (2024-2025-1-计算机基础与程序设计) |
---|---|
这个作业要求在哪里 | ([2024-2025-1计算机基础与程序设计第十三周作业] |
这个作业的目标 | |
作业正文 | (2024-2025-1 学号20241307《计算机基础与程序设计》第十三周学习总结) |
教材学习内容总结
C语言程序设计第十二章的详细总结:
- 结构体的定义和使用
• 结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合,用于将多个相关的数据整合在一起,便于整体操作和管理。
• 结构体的定义使用 struct 关键字,后跟结构体名称和大括号内的成员列表。例如,定义一个表示学生信息的结构体:
struct Student {
char name[20];
int age;
float score;
};
这个结构体包含姓名、年龄和分数三个成员。
2. 结构体变量的初始化
• 结构体变量可以在创建时使用 {} 进行初始化,也可以在创建后使用 . 操作符指定成员变量的值。例如:
struct Student student = {"John Doe", 20, 89.5};
或者在创建后初始化:
struct Student student;
strcpy(student.name, "John Doe");
student.age = 20;
student.score = 89.5;
。
3. 结构体成员的引用
• 使用点运算符 . 访问结构体成员;在结构体嵌套时以级联方式访问数据。例如:
printf("%s\n", student.name);
。
4. 结构体数组
• 结构体数组的定义和使用,即如何存储和操作一组结构体类型的数据。例如:
struct Student students[10];
这定义了一个包含10个 Student 结构体类型元素的数组。
5. 指向结构体的指针
• 指向结构体的指针的定义,以及通过指针访问和操作结构体成员的方法。例如:
struct Student *ptr;
ptr = &student; // ptr现在指向student
printf("%s\n", ptr->name); // 使用 -> 操作符访问成员
。
6. 结构体的嵌套定义
• 一个结构体的成员可以是另一个结构体,这种特性使得可以构建更复杂的数据类型。例如:
struct Date {
int year;
int month;
int day;
};
struct Person {
char name[20];
int age;
struct Date birthdate;
};
。
7. 动态数据结构和动态链表
• 动态链表的构建和操作,是数据结构基础的重要组成部分。这通常涉及到链表的定义、插入、删除等操作。
8. 结构体与文件操作
• 结构体也可以用于文件操作,比如将结构体的数据保存到文件中,或者从文件中读取结构体的数据。这通常涉及到文件I/O函数,如 fopen、fprintf、fscanf 等。
9. 结构体的内存对齐
• 内存对齐的规则和原因,以及如何修改默认对齐数。结构体的大小不是结构体元素单纯相加就行的,因为现代计算机使用的都是32Bit字长的CPU,对这类型的CPU取4个字节的数要比取一个字节要高效,也更方便。这就是内存对齐的由来。
教材学习中的问题和解决过程(先问 AI)
- 问题1:理解结构体内存布局和内存对齐
问题描述:初学者可能难以理解结构体在内存中的布局,尤其是内存对齐和填充的概念。 - 问题1解决方案:
• 学习内存对齐规则:不同的编译器和平台可能有不同的内存对齐规则,但通常编译器会按照特定的边界(如4字节或8字节)对齐结构体的成员。
• 使用sizeof操作符:通过sizeof(struct_name)来查看结构体实际占用的内存大小,这有助于理解内存对齐的影响。
• 使用编译器特定的属性:某些编译器允许你使用属性(如__attribute__((packed)))来控制或禁用内存对齐。 - 问题2结构体与指针的复杂操作
问题描述:在处理指向结构体的指针,尤其是二级指针时,可能会对如何正确地分配和释放内存感到困惑。 - 问题2解决方案:
• 理解指针和地址:确保你理解了指针和地址的概念,以及如何使用&取地址和*解引用。
• 练习动态内存分配:通过编写代码练习malloc和free的使用,特别是对于结构体指针的分配和释放。
• 编写清晰的代码:在分配和释放内存时,编写清晰的代码注释,以避免混淆和内存泄漏。 - 问题3::结构体作为函数参数的传递
问题描述:可能会对如何将结构体作为参数传递给函数,以及这与指针传递的区别感到困惑。 - 问题3解决方案:
• 理解值传递和引用传递:在C语言中,结构体作为参数传递给函数时,实际上是通过值传递的,这意味着函数接收的是结构体的副本。而传递指向结构体的指针则是引用传递,函数可以直接修改原始结构体。
• 使用指针传递:如果需要在函数内部修改结构体的内容,应该传递指向结构体的指针。
• 编写示例代码:通过编写和测试不同的函数调用方式,来加深对值传递和引用传递的理解。
基于AI的学习
代码调试中的问题和解决过程
-
问题1:结构体成员指针未初始化
问题描述:在定义结构体时,如果成员是指针类型而没有初始化,会导致未定义行为,因为指针可能指向任意内存区域。
struct student {
char *name;
int score;
} stu, *pstu;
int main() {
strcpy(stu.name, "Jimy"); // 错误:stu.name 未初始化
stu.score = 99;
return 0;
} -
问题1解决方案:
在使用指针成员之前,为其分配内存空间。
struct student {
char *name;
int score;
} stu, *pstu;
int main() {
stu.name = malloc(64 * sizeof(char)); // 分配内存
strcpy(stu.name, "Jimy");
stu.score = 99;
free(stu.name); // 使用完毕后释放内存
return 0;
} -
问题2:没有为结构体指针分配足够的内存
问题描述:为结构体指针分配内存时,如果分配的大小不正确,也会导致错误。
int main() {
pstu = (struct student)malloc(sizeof(struct student)); // 错误:应该分配 struct student 的大小
strcpy(pstu->name, "Jimy");
pstu->score = 99;
free(pstu);
return 0; -
问题2解决方案:
确保分配的内存大小与结构体的大小相匹配。
int main() {
pstu = (struct student*)malloc(sizeof(struct student)); // 正确分配内存
pstu->name = malloc(64 * sizeof(char)); // 分配内存给指针成员
strcpy(pstu->name, "Jimy");
pstu->score = 99;
free(pstu->name); // 释放指针成员的内存
free(pstu); // 释放结构体的内存
return 0;
} -
问题3: 结构体变量赋值问题(浅拷贝)
问题描述:在将一个结构体变量赋值给另一个结构体变量时,如果结构体包含指针成员,会发生浅拷贝问题,即两个结构体变量共享同一块内存。
struct Person2 {
char* name;
int age;
};
void test2() {
struct Person2 p1;
p1.age = 18;
p1.name = (char)malloc(sizeof(char)64);
strcpy(p1.name, "Tom");
struct Person2 p2;
p2.age = 20;
p2.name = (char)malloc(sizeof(char)128);
strcpy(p2.name, "Jerry");
p1 = p2; // 错误:浅拷贝,p1 和 p2 共享同一块内存
// 释放内存时会导致问题
} -
问题3解决方案:
手动进行深拷贝操作,确保每个指针成员都指向独立的内存区域。
void test2() {
struct Person2 p1;
p1.age = 18;
p1.name = (char)malloc(sizeof(char)64);
strcpy(p1.name, "Tom");
struct Person2 p2;
p2.age = 20;
p2.name = (char)malloc(sizeof(char)128);
strcpy(p2.name, "Jerry");
// 深拷贝
p1.age = p2.age;
p1.name = (char)malloc(sizeof(char)128); // 分配新的内存
strcpy(p1.name, p2.name); // 复制内容
// 释放内存时需要分别释放
free(p2.name);
}
其他(感悟、思考等,可选)
学习 C 语言程序设计第十二章后,我深感其对编程思维的深度拓展。这一章节中诸如文件操作等内容,让我意识到程序不再局限于内存数据的处理,而是能够与外部存储交互,极大地拓宽了程序的功能边界。它教会我如何严谨地组织数据的输入输出流,使程序更具实用性和扩展性。同时,对错误处理机制的深入理解,让我明白程序的健壮性至关重要。我认识到编程不仅是实现功能,更是构建一个稳定、高效、能应对各种情况的系统,每一行代码都肩负着保障整体稳定运行的重任。
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 200/200 | 2/2 | 20/20 | |
第二周 | 300/500 | 4/4 | 18/38 | |
第三周 | 500/1000 | 5/7 | 22/60 | |
第四周 | 500/1300 | 6/9 | 30/90 | |
第五周 | 1000/1400 | 7/9 | 60/90 | |
第六周 | 1200/1500 | 8/9 | 70/90 | |
第七周 | 1400/1600 | 9/10 | 80/100 | |
第八周 | 1600/1700 | 10/11 | 100/100 | |
第九周 | 1900/1900 | 11/11 | 110/110 | |
第十周 | 2100/2100 | 12/12 | 130/130 | |
第十一周 | 2600/2600 | 13/13 | 150/150 | |
第十二周 | 2900/2900 | 14/14 | 170/170 | |
第十三周 | 3500/3500 | 15/15 | 190/190 |