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

更新时间:

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

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

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

在学习 C 语言的过程中,通过实践案例来巩固理论知识是提升编程能力的关键。本文将以「C 练习实例67」为核心,围绕一个具体问题展开分析,帮助读者理解如何将基础语法与实际需求结合。这一实例不仅涉及结构体、指针、动态内存分配等核心知识点,还通过完整案例的拆解,让读者掌握从需求分析到代码实现的完整流程。无论是编程初学者还是希望进阶的开发者,都能通过本文找到适合自己的学习路径。


一、实例背景与目标

假设「C 练习实例67」的任务是:设计一个学生信息管理系统,要求能够动态存储学生姓名、年龄和成绩,并支持增删改查功能。这一场景常见于实际开发,但通过 C 语言实现时,需要综合运用多种技术点。

需求拆解

  1. 动态存储:学生数量未知,需使用动态内存分配(malloc/realloc)。
  2. 结构化数据:学生信息包含多个字段,需用结构体定义数据类型。
  3. 数据操作:实现添加、删除、修改和查询学生信息的功能。

通过这一实例,读者可以掌握以下技能:

  • 结构体的定义与使用
  • 动态内存管理的底层逻辑
  • 指针与数组的灵活操作

二、核心知识点详解

1. 结构体:数据的“集装箱”

结构体(struct)是 C 语言中用于组合不同类型数据的工具。例如,学生信息可以定义为:

struct Student {  
    char name[50];  
    int age;  
    float score;  
};  

比喻:将姓名、年龄、成绩分别装入不同的“抽屉”,再将这些抽屉组合成一个“文件柜”,即为一个结构体。

结构体的使用步骤:

  1. 定义类型struct Student student1;
  2. 访问成员student1.age = 20;
  3. 动态分配:通过指针操作多个结构体对象。

2. 动态内存分配:灵活管理内存

由于学生数量不确定,需用 malloc 分配内存,并通过 realloc 扩容。

关键函数:

  • malloc(size_t size):分配指定字节数的内存空间。
  • realloc(void *ptr, size_t new_size):调整已分配内存的大小。
  • free(void *ptr):释放内存,避免内存泄漏。

示例代码

struct Student *students = NULL;  
int count = 0;  

// 添加新学生时  
students = realloc(students, (count + 1) * sizeof(struct Student));  
if (students == NULL) {  
    printf("内存分配失败!\n");  
    exit(EXIT_FAILURE);  
}  

注意事项:

  • 始终检查 malloc/realloc 返回值是否为 NULL
  • 使用后务必调用 free 释放内存,避免程序占用过多资源。

3. 指针:连接内存与数据的“桥梁”

在动态管理学生信息时,指针是操作内存的关键工具。例如,通过指针遍历学生列表:

for (int i = 0; i < count; i++) {  
    struct Student *current = &students[i];  
    printf("姓名:%s 年龄:%d 成绩:%.1f\n",  
           current->name, current->age, current->score);  
}  

比喻:指针如同“导航仪”,指向内存中的具体位置,帮助程序快速定位和修改数据。

指针与结构体的结合:

  • -> 运算符用于通过指针访问结构体成员。
  • 避免直接操作原始指针,需确保其指向有效内存区域。

三、完整代码实现与解析

1. 主函数框架

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  

#define MAX_NAME_LENGTH 50  

struct Student {  
    char name[MAX_NAME_LENGTH];  
    int age;  
    float score;  
};  

void add_student(struct Student **students, int *count);  
void delete_student(struct Student **students, int *count);  
void modify_student(struct Student **students, int count);  
void search_student(struct Student *students, int count);  

int main() {  
    struct Student *students = NULL;  
    int count = 0;  
    int choice;  

    while (1) {  
        printf("\n学生信息管理系统\n");  
        printf("1. 添加学生\n");  
        printf("2. 删除学生\n");  
        printf("3. 修改学生信息\n");  
        printf("4. 查询学生信息\n");  
        printf("5. 退出\n");  
        printf("请输入选项:");  
        scanf("%d", &choice);  

        switch (choice) {  
            case 1: add_student(&students, &count); break;  
            case 2: delete_student(&students, &count); break;  
            case 3: modify_student(&students, count); break;  
            case 4: search_student(students, count); break;  
            case 5: exit(EXIT_SUCCESS);  
            default: printf("无效选项!\n");  
        }  
    }  
    return 0;  
}  

主函数要点:

  • 使用指针的指针(struct Student **)传递动态数组的地址,以便在函数内修改其内容。
  • 通过 while (1) 循环实现菜单驱动,用户可反复操作。

2. 核心功能函数实现

(1) 添加学生 add_student

void add_student(struct Student **students, int *count) {  
    // 分配新内存空间  
    *students = realloc(*students, (*count + 1) * sizeof(struct Student));  
    if (*students == NULL) {  
        printf("内存不足,无法添加!\n");  
        return;  
    }  

    struct Student *current = &(*students)[*count];  

    printf("请输入姓名:");  
    scanf("%s", current->name);  

    printf("请输入年龄:");  
    scanf("%d", &current->age);  

    printf("请输入成绩:");  
    scanf("%f", &current->score);  

    (*count)++;  
    printf("学生信息添加成功!\n");  
}  

(2) 删除学生 delete_student

void delete_student(struct Student **students, int *count) {  
    if (*count == 0) {  
        printf("当前无学生信息!\n");  
        return;  
    }  

    char target_name[MAX_NAME_LENGTH];  
    printf("请输入要删除学生的姓名:");  
    scanf("%s", target_name);  

    int found = -1;  
    for (int i = 0; i < *count; i++) {  
        if (strcmp((*students)[i].name, target_name) == 0) {  
            found = i;  
            break;  
        }  
    }  

    if (found == -1) {  
        printf("未找到该学生!\n");  
        return;  
    }  

    // 移动数据并释放内存  
    for (int i = found; i < *count - 1; i++) {  
        (*students)[i] = (*students)[i + 1];  
    }  

    *students = realloc(*students, (*count - 1) * sizeof(struct Student));  
    (*count)--;  
    printf("删除成功!\n");  
}  

(3) 查询学生 search_student

void search_student(struct Student *students, int count) {  
    if (count == 0) {  
        printf("当前无学生信息!\n");  
        return;  
    }  

    char target_name[MAX_NAME_LENGTH];  
    printf("请输入要查询学生的姓名:");  
    scanf("%s", target_name);  

    int found = -1;  
    for (int i = 0; i < count; i++) {  
        if (strcmp(students[i].name, target_name) == 0) {  
            found = i;  
            break;  
        }  
    }  

    if (found == -1) {  
        printf("未找到该学生!\n");  
    } else {  
        struct Student *current = &students[found];  
        printf("姓名:%s 年龄:%d 成绩:%.1f\n",  
               current->name, current->age, current->score);  
    }  
}  

3. 关键技术点总结

  1. 动态内存管理:通过 realloc 灵活调整内存大小,避免预分配的局限性。
  2. 结构体与指针的结合:使用 -> 运算符简化对结构体成员的访问。
  3. 字符串操作strcmpscanf 用于比较和输入字符串。

四、常见问题与解决方案

1. 内存泄漏问题

若忘记调用 free,程序可能占用过多内存。解决方案:

  • 在程序结束前释放所有动态分配的内存。
  • 使用调试工具(如 Valgrind)检测内存泄漏。

2. 输入字符串的安全性

直接使用 scanf("%s") 可能导致缓冲区溢出。改进方法:

char buffer[100];  
scanf("%99s", buffer); // 确保不超过数组大小  
strcpy(current->name, buffer);  

3. 界面优化建议

  • 增加数据排序功能(按成绩或年龄排序)。
  • 将学生信息持久化到文件(使用 fopen/fwrite)。

五、结论

通过「C 练习实例67」的实践,我们不仅掌握了结构体、动态内存分配和指针的核心用法,还学会了如何将零散的知识点整合成一个完整的系统。这一过程体现了 C 语言在底层资源控制方面的强大能力,同时也揭示了编程思维的关键:将复杂问题拆解为可管理的模块

对于初学者,建议从代码逐行调试开始,理解每一步内存变化;中级开发者则可尝试扩展功能(如多线程支持或用户界面优化)。通过不断实践,逐步构建自己的“代码工具箱”,最终实现从理论到应用的跨越。

希望本文能成为你学习 C 语言道路上的参考指南,也期待你在编程之路上不断探索与进步!

最新发布