C 练习实例60(建议收藏)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观
在 C 语言的学习过程中,通过实践实例来巩固理论知识是至关重要的。C 练习实例60 是一个典型的数据结构与指针应用案例,它要求开发者设计一个学生信息管理程序,能够动态添加、显示和删除学生记录。这个实例不仅涵盖基础语法,还涉及结构体、指针和动态内存分配等核心概念,是检验编程能力的综合性练习。本文将通过循序渐进的讲解,帮助读者理解其实现逻辑,并掌握相关知识点的实际应用。
知识点概述与目标
在深入分析实例之前,我们需要明确以下核心知识点:
- 结构体(Structure):用于组合不同类型的数据,模拟现实中的复杂对象(如学生信息)。
- 指针(Pointer):用于间接访问内存地址,是动态内存管理和复杂数据操作的基础。
- 动态内存分配(Dynamic Memory Allocation):通过
malloc
和free
函数实现内存的灵活管理。 - 函数参数传递与返回值:理解值传递与指针传递的区别,确保函数间数据交互的高效性。
目标:通过实例实现一个学生管理系统,要求用户能够:
- 动态添加学生记录(姓名、学号、成绩)。
- 显示所有学生信息。
- 根据学号删除特定记录。
实例背景与需求分析
背景
假设我们正在开发一个学校管理系统,需要记录学生的姓名、学号和成绩。由于学生数量不确定,且需要频繁添加或删除记录,使用静态数组显然不够灵活。因此,我们需要借助 动态内存分配 和 结构体 来构建一个可扩展的解决方案。
需求拆解
- 数据结构设计:定义一个
Student
结构体,包含姓名(字符数组)、学号(整数)、成绩(浮点数)。 - 动态存储管理:使用指针数组或链表来存储学生记录,并根据需求动态分配或释放内存。
- 核心功能实现:
add_student()
:动态分配内存并添加新记录。display_students()
:遍历所有记录并打印信息。delete_student()
:根据学号查找并删除记录。
代码实现与详细讲解
第一步:定义结构体与全局变量
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_NAME_LENGTH 50
typedef struct {
char name[MAX_NAME_LENGTH];
int id;
float score;
} Student;
Student* students = NULL; // 存储学生记录的指针数组
int count = 0; // 当前记录数量
int capacity = 0; // 当前内存容量
知识点解析:
- 结构体:
Student
结构体将三个不同数据类型组合成一个逻辑单元,如同一个“学生档案袋”。 - 全局变量:
students
是一个指向Student
类型的指针数组,初始为NULL
;count
和capacity
用于跟踪记录数量和内存容量。
第二步:动态添加学生记录
void add_student() {
// 当内存不足时,扩容
if (count >= capacity) {
capacity = capacity == 0 ? 1 : capacity * 2; // 初始为1,后续翻倍
Student* new_students = (Student*) realloc(students, capacity * sizeof(Student));
if (!new_students) {
printf("Memory allocation failed.\n");
return;
}
students = new_students;
}
// 输入学生信息
printf("Enter name: ");
scanf("%s", students[count].name);
printf("Enter ID: ");
scanf("%d", &students[count].id);
printf("Enter score: ");
scanf("%f", &students[count].score);
count++;
}
关键点解释:
- 动态扩容:通过
realloc
函数动态调整内存空间,避免内存不足的问题。初始容量为0时,realloc(NULL, ...)
等同于malloc
。 - 输入验证:未添加输入校验逻辑(如学号唯一性),可作为扩展练习。
- 结构体成员访问:
students[count].name
通过索引访问数组中的每个结构体成员。
第三步:显示所有学生信息
void display_students() {
if (count == 0) {
printf("No students recorded.\n");
return;
}
printf("\n--- Student List ---\n");
for (int i = 0; i < count; i++) {
printf("Name: %s | ID: %d | Score: %.2f\n",
students[i].name, students[i].id, students[i].score);
}
}
知识点:
- 循环遍历:通过
for
循环遍历students
数组,逐个打印每个学生的字段。 - 格式化输出:
%.2f
保留两位小数,提升输出的可读性。
第四步:根据学号删除记录
void delete_student() {
int target_id;
printf("Enter ID to delete: ");
scanf("%d", &target_id);
int found = -1;
for (int i = 0; i < count; i++) {
if (students[i].id == target_id) {
found = i;
break;
}
}
if (found == -1) {
printf("Student not found.\n");
return;
}
// 删除记录:将后续元素前移
for (int i = found; i < count - 1; i++) {
students[i] = students[i + 1];
}
count--;
// 可选:缩容内存(当容量过剩时)
if (count > 0 && count == capacity / 4) {
capacity = capacity / 2;
Student* new_students = (Student*) realloc(students, capacity * sizeof(Student));
if (new_students) students = new_students;
}
}
核心逻辑:
- 查找目标:遍历数组,找到学号匹配的索引。
- 数据迁移:删除后,将后续元素前移填补空缺。
- 内存管理:当数据量减少到容量的1/4时,通过
realloc
释放多余内存,提升效率。
第五步:主函数与菜单驱动
int main() {
int choice;
while (1) {
printf("\nStudent Management System\n");
printf("1. Add Student\n");
printf("2. Display Students\n");
printf("3. Delete Student\n");
printf("4. Exit\n");
printf("Enter your choice: ");
scanf("%d", &choice);
switch (choice) {
case 1: add_student(); break;
case 2: display_students(); break;
case 3: delete_student(); break;
case 4: exit(0);
default: printf("Invalid choice. Please try again.\n");
}
}
free(students); // 理想情况下,程序结束前应释放内存
return 0;
}
关键点:
- 无限循环:通过
while (1)
循环实现持续交互,直到用户选择退出。 - 资源释放:
free(students)
需在程序结束前调用,避免内存泄漏。
扩展与优化建议
可能的改进方向
- 输入验证:
- 检查学号是否唯一,避免重复记录。
- 限制姓名长度不超过
MAX_NAME_LENGTH
。
- 内存管理优化:
- 在删除操作后,仅当内存利用率低于阈值时才进行缩容,避免频繁
realloc
。
- 在删除操作后,仅当内存利用率低于阈值时才进行缩容,避免频繁
- 文件存储:
将学生数据持久化到文件,避免程序重启后数据丢失。
错误处理增强
- 在
realloc
失败时,添加更友好的提示,并考虑回滚操作。 - 在
scanf
后检查输入是否成功(例如处理非数字输入)。
总结与学习建议
通过 C 练习实例60 的实现,我们掌握了以下技能:
- 结构体的定义与使用,模拟现实中的复杂数据模型。
- 动态内存分配的核心函数
malloc
、realloc
和free
的用法。 - 指针数组的遍历与操作,实现灵活的数据管理。
下一步学习建议:
- 尝试将程序改写为链表形式,对比数组和链表的优缺点。
- 学习文件操作(如
fopen
、fwrite
),实现数据持久化。 - 研究更复杂的算法,如二分查找,优化删除操作的效率。
通过实践此类综合性实例,开发者不仅能巩固 C 语言的基础知识,还能逐步形成解决实际问题的逻辑思维能力。希望本文能成为你探索 C 语言进阶之路的参考指南!