引言:C语言第八章实验的核心意义

在C语言程序设计的学习过程中,第八章通常标志着从基础语法向复杂逻辑和高级应用的转折点。这一阶段的实验内容往往涉及指针、数组、字符串、结构体以及文件操作等核心概念的综合运用。通过本章的实验训练,学习者不仅能够巩固之前的基础知识,还能掌握处理复杂数据结构和算法的能力。

本章实验的核心目标包括:

  1. 指针与数组的深度融合:理解指针算术运算与数组访问的等价性
  2. 字符串处理的实战技巧:掌握字符串函数库的使用和自定义字符串操作
  3. 结构体与共用体的应用:处理复杂数据类型和内存布局
  4. 文件I/O操作:实现数据的持久化存储和读取
  5. 动态内存管理:理解malloc、free等函数的正确使用方式

这些内容构成了C语言程序设计的中级阶段,也是后续学习数据结构和系统编程的重要基础。通过系统的实验训练,学习者能够建立起完整的C语言知识体系,并具备解决实际工程问题的能力。

指针与数组的深度结合实验

实验目标

理解指针与数组的关系,掌握通过指针访问数组元素的方法,并能够处理多维数组的指针操作。

理论基础

在C语言中,数组名本质上是一个指向数组首元素的指针常量。这一特性使得指针与数组在很多情况下可以互换使用,但它们在内存布局和访问方式上存在重要区别。

实战代码示例

#include <stdio.h>

void demonstrate_pointer_array_relationship() {
    int arr[5] = {10, 20, 30, 40, 50};
    int *ptr = arr;  // 指针指向数组首地址
    
    printf("数组访问方式对比:\n");
    printf("arr[2] = %d\n", arr[2]);      // 数组下标访问
    printf("*(arr + 2) = %d\n", *(arr + 2)); // 指针算术访问
    printf("ptr[2] = %d\n", ptr[2]);      // 指针下标访问
    printf("*(ptr + 2) = %d\n", *(ptr + 2)); // 指针算术访问
    
    // 指针遍历数组
    printf("\n指针遍历数组:\n");
    for(int i = 0; i < 5; i++) {
        printf("arr[%d] = %d, *(arr + %d) = %d\n", 
               i, arr[i], i, *(arr + i));
    }
}

void multidimensional_array_pointer() {
    int matrix[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };
    
    // 指向数组的指针
    int (*ptr)[4] = matrix;
    
    printf("\n二维数组的指针访问:\n");
    for(int i = 0; i < 3; i++) {
        for(int j = 0; j < 4; j++) {
            // 三种等价的访问方式
            printf("matrix[%d][%d] = %d, *(*(ptr + %d) + %d) = %d\n",
                   i, j, matrix[i][j], i, j, *(*(ptr + i) + j));
        }
    }
}

int main() {
    demonstrate_pointer_array_relationship();
    multidimensional_array_pointer();
    return 0;
}

常见错误及解决方案

错误1:指针未初始化

int *ptr;  // 野指针
*ptr = 10; // 未定义行为,可能导致程序崩溃

解决方案:始终初始化指针

int *ptr = NULL;  // 初始化为空指针
int arr[5];
ptr = arr;        // 正确赋值

错误2:数组越界访问

int arr[5];
for(int i = 0; i <= 5; i++) {  // 错误:i=5时越界
    arr[i] = i * 10;
}

解决方案:严格检查边界条件

int arr[5];
for(int i = 0; i < 5; i++) {  // 正确:i<5
    arr[i] = i * 10;
}

错误3:指针类型不匹配

int arr[5];
char *ptr = (char *)arr;  // 可能导致数据解释错误

解决方案:保持指针类型一致性

int arr[5];
int *ptr = arr;  // 正确类型匹配

字符串处理与函数库应用实验

实验目标

掌握C语言标准库中的字符串处理函数,并能够编写自定义字符串处理函数。

核心字符串函数详解

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

void string_function_demonstration() {
    char str1[50] = "Hello";
    char str2[50] = "World";
    char str3[50];
    
    // 1. 字符串连接
    strcpy(str3, str1);
    strcat(str3, " ");
    strcat(str3, str2);
    printf("连接结果: %s\n", str3);  // 输出: Hello World
    
    // 2. 字符串比较
    int result = strcmp(str1, str2);
    if(result < 0) {
        printf("%s 小于 %s\n", str1, str2);
    } else if(result > 0) {
        printf("%s 大于 %s\n", str1, str2);
    } else {
        printf("%s 等于 %s\n", str1, str2);
    }
    
    // 3. 字符串长度
    size_t len = strlen(str3);
    printf("字符串长度: %zu\n", len);
    
    // 4. 字符串查找
    char *pos = strchr(str3, 'W');  // 查找字符
    if(pos != NULL) {
        printf("找到 'W' 在位置: %ld\n", pos - str3);
    }
    
    // 5. 子字符串查找
    char *subpos = strstr(str3, "World");  // 查找子串
    if(subpos != NULL) {
        printf("找到 'World' 在位置: %ld\n", subpos - str3);
    }
}

// 自定义字符串复制函数
char *my_strcpy(char *dest, const char *src) {
    if(dest == NULL || src == NULL) {
        return NULL;
    }
    
    char *original_dest = dest;
    while((*dest++ = *src++) != '\0');
    
    return original_dest;
}

// 自定义字符串长度函数
size_t my_strlen(const char *str) {
    if(str == NULL) {
        return 0;
    }
    
    const char *ptr = str;
    while(*ptr != '\0') {
        ptr++;
    }
    return ptr - str;
}

// 自定义字符串反转函数
void reverse_string(char *str) {
    if(str == NULL) return;
    
    size_t len = strlen(str);
    char *start = str;
    char *end = str + len - 1;
    
    while(start < end) {
        char temp = *start;
        *start = *end;
        *end = temp;
        start++;
        end--;
    }
}

void custom_string_functions_test() {
    char test_str[50] = "Programming";
    
    printf("\n自定义函数测试:\n");
    printf("原字符串: %s\n", test_str);
    printf("长度: %zu\n", my_strlen(test_str));
    
    reverse_string(test_str);
    printf("反转后: %s\n", test_str);
    
    char copy[50];
    my_strcpy(copy, "Hello");
    printf("复制结果: %s\n", copy);
}

int main() {
    string_function_demonstration();
    custom_string_functions_test();
    return 0;
}

常见错误及解决方案

错误1:字符串未以’\0’结尾

char str[5];
str[0] = 'H';
str[1] = 'e';
str[2] = 'l';
str[3] = 'l';
str[4] = 'o';  // 缺少'\0',导致字符串函数越界访问

解决方案

char str[6] = "Hello";  // 自动包含'\0'
// 或者
char str[5];
strcpy(str, "Hello");   // 自动添加'\0'

错误2:缓冲区溢出

char str[5];
strcpy(str, "Hello World");  // 溢出!

解决方案

char str[50];
strncpy(str, "Hello World", sizeof(str) - 1);
str[sizeof(str) - 1] = '\0';  // 确保终止符

错误3:修改字符串字面量

char *str = "Hello";
str[0] = 'h';  // 未定义行为!字符串字面量是只读的

解决方案

char str[] = "Hello";  // 在栈上创建可修改的副本
str[0] = 'h';          // 正确

结构体与共用体综合应用实验

实验目标

掌握结构体的定义、初始化、访问方式,以及结构体数组和指针的使用。理解共用体的内存布局特点。

实战代码示例

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

// 定义学生结构体
typedef struct {
    char name[50];
    int age;
    float score;
    char student_id[20];
} Student;

// 定义共用体
typedef union {
    int int_value;
    float float_value;
    char char_value;
} NumericUnion;

// 嵌套结构体
typedef struct {
    char street[50];
    char city[50];
    char zip[10];
} Address;

typedef struct {
    char name[50];
    int age;
    Address address;  // 嵌套结构体
} Person;

void structure_basic_operations() {
    // 结构体初始化
    Student s1 = {"张三", 20, 85.5, "2023001"};
    Student s2;
    
    // 结构体成员访问
    strcpy(s2.name, "李四");
    s2.age = 21;
    s2.score = 92.0;
    strcpy(s2.student_id, "2023002");
    
    printf("学生1: %s, 年龄: %d, 分数: %.1f\n", 
           s1.name, s1.age, s1.score);
    printf("学生2: %s, 年龄: %d, 分数: %.1f\n", 
           s2.name, s2.age, s2.score);
}

void structure_array_operations() {
    Student class[3] = {
        {"王五", 19, 78.0, "2023003"},
        {"赵六", 20, 88.5, "2023004"},
        {"钱七", 22, 95.0, "2023005"}
    };
    
    // 结构体数组遍历
    printf("\n班级学生信息:\n");
    for(int i = 0; i < 3; i++) {
        printf("学号: %s, 姓名: %s, 年龄: %d, 分数: %.1f\n",
               class[i].student_id, class[i].name, 
               class[i].age, class[i].score);
    }
    
    // 结构体指针
    Student *ptr = class;
    printf("\n通过指针访问:\n");
    for(int i = 0; i < 3; i++, ptr++) {
        printf("指针方式 - 姓名: %s, 分数: %.1f\n", 
               ptr->name, ptr->score);
    }
}

void union_demonstration() {
    NumericUnion u;
    
    printf("\n共用体内存共享特性:\n");
    u.int_value = 65;
    printf("设置int_value = 65\n");
    printf("int_value: %d\n", u.int_value);
    printf("char_value: %c (解释为ASCII字符)\n", u.char_value);
    
    u.float_value = 3.14f;
    printf("\n设置float_value = 3.14\n");
    printf("float_value: %.2f\n", u.float_value);
    printf("int_value: %d (内存被覆盖)\n", u.int_value);
    
    // 共用体大小验证
    printf("\n共用体大小: %zu字节\n", sizeof(u));
    printf("int大小: %zu, float大小: %zu\n", 
           sizeof(u.int_value), sizeof(u.float_value));
}

void nested_structure_operations() {
    Person p = {
        "张伟",
        25,
        {"人民路123号", "北京市", "100000"}
    };
    
    printf("\n嵌套结构体:\n");
    printf("姓名: %s\n", p.name);
    printf("地址: %s, %s, %s\n", 
           p.address.street, p.address.city, p.address.zip);
}

int main() {
    structure_basic_operations();
    structure_array_operations();
    union_demonstration();
    nested_structure_operations();
    return 0;
}

常见错误及解决方案

错误1:结构体比较错误

Student s1 = {"张三", 20, 85.5, "2023001"};
Student s2 = {"张三", 20, 85.5, "2023001"};
if(s1 == s2) {  // 错误!不能直接比较结构体
    // ...
}

解决方案

// 逐个成员比较
int equal = (strcmp(s1.name, s2.name) == 0) &&
            (s1.age == s2.age) &&
            (s1.score == s2.score) &&
            (strcmp(s1.student_id, s2.student_id) == 0);

错误2:结构体赋值时的内存问题

Student s1 = {"张三", 20, 85.5, "2023001"};
Student s2;
s2 = s1;  // 正确,但如果是动态分配的结构体指针则需要深拷贝

错误3:共用体使用不当

NumericUnion u;
u.int_value = 65;
printf("%f", u.float_value);  // 错误!读取未写入的成员

解决方案:始终跟踪共用体当前存储的数据类型

文件操作与数据持久化实验

实验目标

掌握文件的打开、读写、关闭操作,理解文本文件和二进制文件的区别,实现数据的持久化存储。

实战代码示例

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

// 学生信息写入文件
void write_students_to_file() {
    FILE *fp;
    Student class[3] = {
        {"张三", 20, 85.5, "2023001"},
        {"李四", 21, 92.0, "2023002"},
        {"王五", 19, 78.5, "2023003"}
    };
    
    // 写入二进制文件
    fp = fopen("students.dat", "wb");
    if(fp == NULL) {
        printf("无法创建文件\n");
        return;
    }
    
    fwrite(class, sizeof(Student), 3, fp);
    fclose(fp);
    printf("学生数据已写入二进制文件\n");
}

// 从文件读取学生信息
void read_students_from_file() {
    FILE *fp;
    Student s;
    
    fp = fopen("students.dat", "rb");
    if(fp == NULL) {
        printf("无法打开文件\n");
        return;
    }
    
    printf("\n从文件读取的学生信息:\n");
    while(fread(&s, sizeof(Student), 1, fp) == 1) {
        printf("学号: %s, 姓名: %s, 年龄: %d, 分数: %.1f\n",
               s.student_id, s.name, s.age, s.score);
    }
    
    fclose(fp);
}

// 文本文件操作示例
void text_file_operations() {
    FILE *fp;
    char buffer[100];
    
    // 写入文本文件
    fp = fopen("report.txt", "w");
    if(fp == NULL) {
        printf("无法创建文本文件\n");
        return;
    }
    
    fprintf(fp, "学生成绩报告\n");
    fprintf(fp, "================\n");
    fprintf(fp, "张三: 85.5分\n");
    fprintf(fp, "李四: 92.0分\n");
    fprintf(fp, "王五: 78.5分\n");
    fclose(fp);
    
    // 读取文本文件
    fp = fopen("report.txt", "r");
    if(fp == NULL) {
        printf("无法打开文本文件\n");
        return;
    }
    
    printf("\n文本文件内容:\n");
    while(fgets(buffer, sizeof(buffer), fp) != NULL) {
        printf("%s", buffer);
    }
    fclose(fp);
}

// 文件追加操作
void append_to_file() {
    FILE *fp = fopen("report.txt", "a");
    if(fp == NULL) {
        printf("无法打开文件进行追加\n");
        return;
    }
    
    fprintf(fp, "赵六: 88.0分\n");
    fclose(fp);
    printf("\n已追加新数据到文件\n");
}

// 文件指针定位操作
void file_pointer_operations() {
    FILE *fp = fopen("students.dat", "rb");
    if(fp == NULL) {
        printf("无法打开文件\n");
        return;
    }
    
    // 移动到第二个学生记录
    fseek(fp, sizeof(Student), SEEK_SET);
    
    Student s;
    if(fread(&s, sizeof(Student), 1, fp) == 1) {
        printf("\n第二个学生信息:\n");
        printf("姓名: %s, 分数: %.1f\n", s.name, s.score);
    }
    
    // 移动到文件末尾
    fseek(fp, 0, SEEK_END);
    long size = ftell(fp);
    printf("文件大小: %ld字节\n", size);
    
    fclose(fp);
}

int main() {
    write_students_to_file();
    read_students_from_file();
    text_file_operations();
    append_to_file();
    file_pointer_operations();
    return 0;
}

常见错误及解决方案

错误1:文件打开模式错误

FILE *fp = fopen("data.txt", "r");
fprintf(fp, "写入数据");  // 错误!只读模式不能写入

解决方案

FILE *fp = fopen("data.txt", "w");  // 写入模式
if(fp != NULL) {
    fprintf(fp, "写入数据");
    fclose(fp);
}

错误2:忘记检查文件指针

FILE *fp = fopen("nonexistent.txt", "r");
fscanf(fp, "%d", &num);  // 如果文件不存在,fp为NULL,程序崩溃

解决方案

FILE *fp = fopen("data.txt", "r");
if(fp == NULL) {
    printf("文件打开失败\n");
    return -1;
}
// 正常操作
fclose(fp);

错误3:二进制文件与文本文件混淆

// 错误:用文本模式处理二进制数据
FILE *fp = fopen("data.bin", "w");  // 应该用"wb"
Student s = {"张三", 20, 85.5, "2023001"};
fwrite(&s, sizeof(Student), 1, fp);  // 可能出现数据损坏

动态内存管理实验

实验目标

理解动态内存分配的原理,掌握malloc、calloc、realloc和free函数的使用,避免内存泄漏。

实战代码示例

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

// 动态创建学生数组
Student *create_dynamic_student_array(int size) {
    if(size <= 0) {
        printf("无效的数组大小\n");
        return NULL;
    }
    
    Student *arr = (Student *)malloc(size * sizeof(Student));
    if(arr == NULL) {
        printf("内存分配失败\n");
        return NULL;
    }
    
    // 初始化
    for(int i = 0; i < size; i++) {
        strcpy(arr[i].name, "");
        arr[i].age = 0;
        arr[i].score = 0.0;
        strcpy(arr[i].student_id, "");
    }
    
    return arr;
}

// 动态字符串处理
void dynamic_string_operations() {
    char *str = (char *)malloc(50 * sizeof(char));
    if(str == NULL) {
        printf("内存分配失败\n");
        return;
    }
    
    strcpy(str, "Hello Dynamic Memory");
    printf("动态字符串: %s\n", str);
    
    // 重新分配内存
    char *new_str = (char *)realloc(str, 100 * sizeof(char));
    if(new_str != NULL) {
        str = new_str;
        strcat(str, " - Extended");
        printf("扩展后: %s\n", str);
    }
    
    free(str);
}

// 动态结构体数组管理
void dynamic_structure_management() {
    int n;
    printf("\n请输入学生数量: ");
    scanf("%d", &n);
    
    Student *students = create_dynamic_student_array(n);
    if(students == NULL) {
        return;
    }
    
    // 输入学生数据
    for(int i = 0; i < n; i++) {
        printf("\n学生 %d:\n", i + 1);
        printf("姓名: ");
        scanf("%s", students[i].name);
        printf("年龄: ");
        scanf("%d", &students[i].age);
        printf("分数: ");
        scanf("%f", &students[i].score);
        printf("学号: ");
        scanf("%s", students[i].student_id);
    }
    
    // 显示学生数据
    printf("\n学生信息列表:\n");
    for(int i = 0; i < n; i++) {
        printf("%s (%d岁) - 分数: %.1f - 学号: %s\n",
               students[i].name, students[i].age,
               students[i].score, students[i].student_id);
    }
    
    // 释放内存
    free(students);
    printf("\n内存已释放\n");
}

// 二维动态数组
void dynamic_2d_array() {
    int rows = 3, cols = 4;
    
    // 分配行指针数组
    int **matrix = (int **)malloc(rows * sizeof(int *));
    if(matrix == NULL) {
        printf("内存分配失败\n");
        return;
    }
    
    // 为每一行分配内存
    for(int i = 0; i < rows; i++) {
        matrix[i] = (int *)malloc(cols * sizeof(int));
        if(matrix[i] == NULL) {
            printf("第%d行内存分配失败\n", i);
            // 释放已分配的内存
            for(int j = 0; j < i; j++) {
                free(matrix[j]);
            }
            free(matrix);
            return;
        }
    }
    
    // 初始化和使用
    for(int i = 0; i < rows; i++) {
        for(int j = 0; j < cols; j++) {
            matrix[i][j] = i * cols + j;
            printf("%2d ", matrix[i][j]);
        }
        printf("\n");
    }
    
    // 释放内存(注意顺序)
    for(int i = 0; i < rows; i++) {
        free(matrix[i]);
    }
    free(matrix);
}

// 内存泄漏检测示例
void memory_leak_example() {
    // 错误示例:忘记释放内存
    int *data = (int *)malloc(100 * sizeof(int));
    // ... 使用data
    // 忘记 free(data);  // 内存泄漏!
    
    // 正确示例
    int *data_correct = (int *)malloc(100 * sizeof(int));
    if(data_correct != NULL) {
        // ... 使用data_correct
        free(data_correct);  // 及时释放
        data_correct = NULL; // 避免悬空指针
    }
}

int main() {
    dynamic_string_operations();
    dynamic_structure_management();
    dynamic_2d_array();
    memory_leak_example();
    return 0;
}

常见错误及解决方案

错误1:内存泄漏

void leak_function() {
    int *ptr = (int *)malloc(100 * sizeof(int));
    // 使用ptr
    // 忘记 free(ptr);  // 每次调用都会泄漏内存
}

解决方案:确保每个malloc都有对应的free

错误2:重复释放

int *ptr = (int *)malloc(sizeof(int));
free(ptr);
free(ptr);  // 错误!重复释放导致崩溃

解决方案

free(ptr);
ptr = NULL;  // 释放后置空

错误3:释放后继续使用

int *ptr = (int *)malloc(sizeof(int));
*ptr = 10;
free(ptr);
printf("%d", *ptr);  // 错误!悬空指针

解决方案

free(ptr);
ptr = NULL;  // 避免误用

综合实战项目:学生成绩管理系统

项目概述

实现一个完整的学生成绩管理系统,综合运用本章所有知识点。

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

#define MAX_STUDENTS 100
#define FILENAME "students.dat"

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

// 全局变量
Student *students = NULL;
int student_count = 0;
int capacity = 0;

// 函数声明
void init_system();
void add_student();
void display_all();
void search_student();
void delete_student();
void save_to_file();
void load_from_file();
void sort_students();
void free_memory();

// 初始化系统
void init_system() {
    students = (Student *)malloc(10 * sizeof(Student));
    if(students == NULL) {
        printf("内存初始化失败\n");
        exit(1);
    }
    capacity = 10;
    student_count = 0;
}

// 扩展数组容量
void expand_capacity() {
    if(student_count >= capacity) {
        capacity *= 2;
        Student *new_students = (Student *)realloc(students, capacity * sizeof(Student));
        if(new_students == NULL) {
            printf("内存扩展失败\n");
            return;
        }
        students = new_students;
        printf("系统容量已扩展至 %d\n", capacity);
    }
}

// 添加学生
void add_student() {
    expand_capacity();
    
    Student s;
    printf("\n添加新学生:\n");
    printf("姓名: ");
    scanf("%s", s.name);
    printf("年龄: ");
    scanf("%d", &s.age);
    printf("分数: ");
    scanf("%f", &s.score);
    printf("学号: ");
    scanf("%s", s.student_id);
    
    students[student_count] = s;
    student_count++;
    printf("学生添加成功!\n");
}

// 显示所有学生
void display_all() {
    if(student_count == 0) {
        printf("\n没有学生记录\n");
        return;
    }
    
    printf("\n%-15s %-10s %-8s %s\n", "姓名", "学号", "年龄", "分数");
    printf("------------------------------------------------\n");
    for(int i = 0; i < student_count; i++) {
        printf("%-15s %-10s %-8d %.1f\n", 
               students[i].name, students[i].student_id,
               students[i].age, students[i].score);
    }
}

// 搜索学生
void search_student() {
    if(student_count == 0) {
        printf("\n没有学生记录\n");
        return;
    }
    
    char search_id[20];
    printf("\n请输入要搜索的学号: ");
    scanf("%s", search_id);
    
    for(int i = 0; i < student_count; i++) {
        if(strcmp(students[i].student_id, search_id) == 0) {
            printf("\n找到学生:\n");
            printf("姓名: %s\n", students[i].name);
            printf("学号: %s\n", students[i].student_id);
            printf("年龄: %d\n", students[i].age);
            printf("分数: %.1f\n", students[i].score);
            return;
        }
    }
    
    printf("未找到学号为 %s 的学生\n", search_id);
}

// 删除学生
void delete_student() {
    if(student_count == 0) {
        printf("\n没有学生记录\n");
        return;
    }
    
    char delete_id[20];
    printf("\n请输入要删除的学号: ");
    scanf("%s", delete_id);
    
    int index = -1;
    for(int i = 0; i < student_count; i++) {
        if(strcmp(students[i].student_id, delete_id) == 0) {
            index = i;
            break;
        }
    }
    
    if(index == -1) {
        printf("未找到学号为 %s 的学生\n", delete_id);
        return;
    }
    
    // 移动后续元素
    for(int i = index; i < student_count - 1; i++) {
        students[i] = students[i + 1];
    }
    
    student_count--;
    printf("学生删除成功!\n");
}

// 保存到文件
void save_to_file() {
    FILE *fp = fopen(FILENAME, "wb");
    if(fp == NULL) {
        printf("无法打开文件进行保存\n");
        return;
    }
    
    // 先保存学生数量
    fwrite(&student_count, sizeof(int), 1, fp);
    // 保存学生数据
    fwrite(students, sizeof(Student), student_count, fp);
    fclose(fp);
    
    printf("数据已保存到 %s\n", FILENAME);
}

// 从文件加载
void load_from_file() {
    FILE *fp = fopen(FILENAME, "rb");
    if(fp == NULL) {
        printf("没有找到保存文件,开始新的系统\n");
        return;
    }
    
    // 读取学生数量
    int count;
    if(fread(&count, sizeof(int), 1, fp) != 1) {
        printf("文件读取失败\n");
        fclose(fp);
        return;
    }
    
    // 检查容量
    while(count > capacity) {
        expand_capacity();
    }
    
    // 读取学生数据
    if(fread(students, sizeof(Student), count, fp) == count) {
        student_count = count;
        printf("已从文件加载 %d 条学生记录\n", count);
    } else {
        printf("数据加载不完整\n");
    }
    
    fclose(fp);
}

// 排序学生(按分数降序)
void sort_students() {
    if(student_count < 2) {
        printf("\n学生数量不足,无需排序\n");
        return;
    }
    
    for(int i = 0; i < student_count - 1; i++) {
        for(int j = 0; j < student_count - 1 - i; j++) {
            if(students[j].score < students[j + 1].score) {
                // 交换
                Student temp = students[j];
                students[j] = students[j + 1];
                students[j + 1] = temp;
            }
        }
    }
    
    printf("\n学生已按分数降序排列\n");
}

// 释放内存
void free_memory() {
    if(students != NULL) {
        free(students);
        students = NULL;
    }
    student_count = 0;
    capacity = 0;
    printf("\n内存已释放,系统关闭\n");
}

// 主菜单
void show_menu() {
    printf("\n========== 学生成绩管理系统 ==========\n");
    printf("1. 添加学生\n");
    printf("2. 显示所有学生\n");
    printf("3. 搜索学生\n");
    printf("4. 删除学生\n");
    printf("5. 排序学生\n");
    printf("6. 保存数据\n");
    printf("7. 加载数据\n");
    printf("0. 退出系统\n");
    printf("======================================\n");
    printf("请选择操作: ");
}

int main() {
    int choice;
    
    init_system();
    load_from_file();
    
    while(1) {
        show_menu();
        if(scanf("%d", &choice) != 1) {
            // 清除错误输入
            while(getchar() != '\n');
            printf("无效输入,请重新选择\n");
            continue;
        }
        
        switch(choice) {
            case 1:
                add_student();
                break;
            case 2:
                display_all();
                break;
            case 3:
                search_student();
                break;
            case 4:
                delete_student();
                break;
            case 5:
                sort_students();
                break;
            case 6:
                save_to_file();
                break;
            case 7:
                load_from_file();
                break;
            case 0:
                save_to_file();
                free_memory();
                printf("感谢使用!\n");
                return 0;
            default:
                printf("无效选择,请重新输入\n");
        }
    }
    
    return 0;
}

调试技巧与性能优化

调试技巧

  1. 使用printf调试
#define DEBUG 1

#ifdef DEBUG
#define DBG_PRINT(fmt, ...) printf("DEBUG: " fmt, ##__VA_ARGS__)
#else
#define DBG_PRINT(fmt, ...)
#endif

void debug_example() {
    int *ptr = malloc(sizeof(int));
    DBG_PRINT("分配内存地址: %p\n", ptr);
    *ptr = 10;
    DBG_PRINT("赋值: %d\n", *ptr);
    free(ptr);
    DBG_PRINT("内存已释放\n");
}
  1. 内存检测工具
// 使用valgrind检测内存泄漏
// 编译时添加调试信息:gcc -g program.c -o program
// 运行:valgrind --leak-check=full ./program

性能优化建议

  1. 减少不必要的内存分配
// 不好的做法
for(int i = 0; i < 1000; i++) {
    char *temp = malloc(100);
    // 使用temp
    free(temp);
}

// 好的做法
char temp[100];  // 使用栈内存
for(int i = 0; i < 1000; i++) {
    // 使用temp
}
  1. 批量操作优化
// 批量读取文件
void optimized_file_read() {
    FILE *fp = fopen("data.bin", "rb");
    if(!fp) return;
    
    // 一次性读取多个记录
    Student buffer[100];
    size_t count;
    while((count = fread(buffer, sizeof(Student), 100, fp)) > 0) {
        // 处理buffer中的count个记录
    }
    fclose(fp);
}

总结

本章实验涵盖了C语言中级编程的核心内容,从指针与数组的深度结合到复杂的结构体管理,再到文件操作和动态内存管理。通过这些实验,学习者应该能够:

  1. 熟练掌握指针操作:理解指针与数组的关系,能够正确使用指针遍历和操作数组
  2. 精通字符串处理:掌握标准库函数和自定义字符串操作
  3. 熟练运用结构体:处理复杂数据类型,包括嵌套结构体和共用体
  4. 掌握文件I/O:实现数据的持久化存储和读取
  5. 正确管理内存:避免内存泄漏、悬空指针等常见问题

这些技能构成了C语言程序设计的坚实基础,为后续学习高级数据结构和系统编程打下了重要基础。在实际开发中,务必注意代码的健壮性和内存安全性,养成良好的编程习惯。