C 练习实例90(一文讲透)

更新时间:

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

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

  • 新开坑项目:《Spring AI 项目实战》 正在持续爆肝中,基于 Spring AI + Spring Boot 3.x + JDK 21..., 点击查看 ;
  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 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 练习实例90 作为进阶阶段的典型题目,通常涉及结构体、指针、文件操作等综合知识点。通过这类实例的深入分析,初学者和中级开发者不仅能加深对核心概念的理解,还能掌握如何将零散的知识点串联成完整的程序逻辑。本文将通过案例解析、代码演示和知识点拆解,帮助读者逐步攻克这一挑战。


一、知识点解析:结构体与指针的协同工作

1.1 结构体(Struct):数据的“容器”

结构体是 C 语言中自定义数据类型的重要工具,可以将不同类型的数据组合成一个逻辑单元。例如,假设我们需要管理学生信息:

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

比喻:结构体就像一个定制的收纳盒,将姓名、年龄、成绩等不同“物品”(变量)整齐地存放在一起。

1.2 指针(Pointer):地址的“导航仪”

指针存储变量的内存地址,是 C 语言中灵活操作数据的核心工具。例如,通过指针可以访问结构体变量的成员:

struct Student student1;  
struct Student *ptr = &student1;  
printf("Name: %s\n", (*ptr).name); // 或者更简洁的写法:ptr->name  

比喻:指针如同地图上的坐标,帮助开发者精准定位数据的位置,并进行高效操作。

1.3 结构体与指针的结合

C 练习实例90 中,结构体和指针常被用于动态管理数据。例如,通过指针数组存储多个学生信息:

struct Student *students[100]; // 可存储最多 100 个指向 Student 的指针  

逻辑扩展:这种设计允许程序根据实际需求动态分配内存,避免了静态数组的容量限制。


二、实例代码详解:以学生管理系统为例

2.1 需求分析

假设 C 练习实例90 的目标是:

  1. 定义一个结构体 Student,包含姓名、年龄、成绩。
  2. 使用指针动态管理学生数据。
  3. 将学生信息写入文件并读取。

2.2 代码分步解析

步骤1:定义结构体和函数

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

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

void add_student(struct Student **students, int *count);  
void save_to_file(struct Student *students[], int count);  
void load_from_file(struct Student ***students, int *count);  

关键点

  • struct Student **students 是指向指针的指针,用于动态扩容。
  • int *count 通过指针传递当前学生数量,避免函数内部修改无效。

步骤2:添加学生数据

void add_student(struct Student **students, int *count) {  
    // 动态分配新内存  
    struct Student *new_student = (struct Student *)malloc(sizeof(struct Student));  
    printf("Enter name: ");  
    scanf("%s", new_student->name);  
    printf("Enter age: ");  
    scanf("%d", &new_student->age);  
    printf("Enter GPA: ");  
    scanf("%f", &new_student->gpa);  

    // 将新指针追加到数组  
    students[*count] = new_student;  
    (*count)++;  
}  

逻辑解析

  • 使用 malloc 动态分配内存,避免静态数组的容量限制。
  • 通过 students[*count] 将新指针存入数组,并更新计数器。

步骤3:文件读写操作

void save_to_file(struct Student *students[], int count) {  
    FILE *file = fopen("students.dat", "wb");  
    if (file == NULL) {  
        printf("Error opening file.\n");  
        return;  
    }  
    fwrite(students, sizeof(struct Student *), count, file); // 存储指针数组  
    fclose(file);  
}  

void load_from_file(struct Student ***students, int *count) {  
    FILE *file = fopen("students.dat", "rb");  
    if (file == NULL) {  
        *count = 0;  
        return;  
    }  
    fseek(file, 0, SEEK_END);  
    long size = ftell(file);  
    *count = size / sizeof(struct Student *);  
    *students = (struct Student **)malloc(*count * sizeof(struct Student *));  
    fseek(file, 0, SEEK_SET);  
    fread(*students, sizeof(struct Student *), *count, file);  
    fclose(file);  
}  

关键点

  • 使用二进制文件(wb/rb)存储指针数组,保留内存地址信息。
  • fseekftell 用于计算文件中的数据量,确保读取的准确性。

三、案例分析:程序的完整流程与调试

3.1 主函数逻辑

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

    while (1) {  
        printf("\n1. Add Student\n2. Save Data\n3. Load Data\n4. Exit\n");  
        int choice;  
        scanf("%d", &choice);  

        switch(choice) {  
            case 1:  
                add_student(students, &count);  
                break;  
            case 2:  
                save_to_file(students, count);  
                break;  
            case 3:  
                load_from_file(&students, &count);  
                break;  
            case 4:  
                exit(0);  
        }  
    }  
    return 0;  
}  

注意事项

  • 初始时 students 被设为 NULL,通过 load_from_file 动态分配内存。
  • 指针的传递需格外小心,避免内存泄漏或野指针。

3.2 常见问题与调试技巧

  • 问题1:文件读取后数据异常。
    原因:二进制文件存储的是内存地址,不同程序运行时地址可能变化。
    解决方案:改用存储结构体内容而非指针,或通过其他方式重建数据。

  • 问题2:内存未释放导致泄漏。
    解决方案:在程序结束前遍历 students 数组,逐个 free 每个 Student 对象。


四、知识扩展:从实例到实际应用

4.1 结构体的嵌套与复杂场景

在更复杂的系统中,结构体可以嵌套使用。例如,添加一个 Course 结构体来记录学生的课程成绩:

struct Course {  
    char course_name[50];  
    float score;  
};  

struct Student {  
    char name[50];  
    struct Course courses[10]; // 学生可选修多门课程  
};  

4.2 文件操作的优化方向

  • 文本文件替代二进制文件:使用 fprintffscanf 存储可读性更高的文本数据。
  • 错误处理增强:添加对无效输入或文件权限问题的检测。

五、结论:通过实例深化理解

C 练习实例90 通过结构体、指针和文件操作的结合,展现了 C 语言在数据管理和持久化存储中的强大能力。对于初学者而言,这类实例不仅是代码的练习,更是逻辑思维的训练场。通过逐步拆解需求、理解指针的灵活用法,以及掌握文件操作的细节,开发者能够构建出更复杂、健壮的程序。

建议读者在完成本实例后,尝试以下扩展:

  1. 添加学生信息的排序功能(按 GPA 或年龄)。
  2. 使用链表替代指针数组,进一步优化动态内存管理。

通过持续实践和案例分析,C 语言的核心概念将逐渐内化为解决问题的工具,帮助开发者在实际开发中游刃有余。

最新发布