C 练习实例60(建议收藏)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观

在 C 语言的学习过程中,通过实践实例来巩固理论知识是至关重要的。C 练习实例60 是一个典型的数据结构与指针应用案例,它要求开发者设计一个学生信息管理程序,能够动态添加、显示和删除学生记录。这个实例不仅涵盖基础语法,还涉及结构体、指针和动态内存分配等核心概念,是检验编程能力的综合性练习。本文将通过循序渐进的讲解,帮助读者理解其实现逻辑,并掌握相关知识点的实际应用。


知识点概述与目标

在深入分析实例之前,我们需要明确以下核心知识点:

  1. 结构体(Structure):用于组合不同类型的数据,模拟现实中的复杂对象(如学生信息)。
  2. 指针(Pointer):用于间接访问内存地址,是动态内存管理和复杂数据操作的基础。
  3. 动态内存分配(Dynamic Memory Allocation):通过 mallocfree 函数实现内存的灵活管理。
  4. 函数参数传递与返回值:理解值传递与指针传递的区别,确保函数间数据交互的高效性。

目标:通过实例实现一个学生管理系统,要求用户能够:

  • 动态添加学生记录(姓名、学号、成绩)。
  • 显示所有学生信息。
  • 根据学号删除特定记录。

实例背景与需求分析

背景

假设我们正在开发一个学校管理系统,需要记录学生的姓名、学号和成绩。由于学生数量不确定,且需要频繁添加或删除记录,使用静态数组显然不够灵活。因此,我们需要借助 动态内存分配结构体 来构建一个可扩展的解决方案。

需求拆解

  1. 数据结构设计:定义一个 Student 结构体,包含姓名(字符数组)、学号(整数)、成绩(浮点数)。
  2. 动态存储管理:使用指针数组或链表来存储学生记录,并根据需求动态分配或释放内存。
  3. 核心功能实现
    • 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 类型的指针数组,初始为 NULLcountcapacity 用于跟踪记录数量和内存容量。

第二步:动态添加学生记录

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++;  
}  

关键点解释

  1. 动态扩容:通过 realloc 函数动态调整内存空间,避免内存不足的问题。初始容量为0时,realloc(NULL, ...) 等同于 malloc
  2. 输入验证:未添加输入校验逻辑(如学号唯一性),可作为扩展练习。
  3. 结构体成员访问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. 查找目标:遍历数组,找到学号匹配的索引。
  2. 数据迁移:删除后,将后续元素前移填补空缺。
  3. 内存管理:当数据量减少到容量的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) 需在程序结束前调用,避免内存泄漏。

扩展与优化建议

可能的改进方向

  1. 输入验证
    • 检查学号是否唯一,避免重复记录。
    • 限制姓名长度不超过 MAX_NAME_LENGTH
  2. 内存管理优化
    • 在删除操作后,仅当内存利用率低于阈值时才进行缩容,避免频繁 realloc
  3. 文件存储
    将学生数据持久化到文件,避免程序重启后数据丢失。

错误处理增强

  • realloc 失败时,添加更友好的提示,并考虑回滚操作。
  • scanf 后检查输入是否成功(例如处理非数字输入)。

总结与学习建议

通过 C 练习实例60 的实现,我们掌握了以下技能:

  1. 结构体的定义与使用,模拟现实中的复杂数据模型。
  2. 动态内存分配的核心函数 mallocreallocfree 的用法。
  3. 指针数组的遍历与操作,实现灵活的数据管理。

下一步学习建议

  • 尝试将程序改写为链表形式,对比数组和链表的优缺点。
  • 学习文件操作(如 fopenfwrite),实现数据持久化。
  • 研究更复杂的算法,如二分查找,优化删除操作的效率。

通过实践此类综合性实例,开发者不仅能巩固 C 语言的基础知识,还能逐步形成解决实际问题的逻辑思维能力。希望本文能成为你探索 C 语言进阶之路的参考指南!

最新发布