引言:C语言第八章实验的核心意义
在C语言程序设计的学习过程中,第八章通常标志着从基础语法向复杂逻辑和高级应用的转折点。这一阶段的实验内容往往涉及指针、数组、字符串、结构体以及文件操作等核心概念的综合运用。通过本章的实验训练,学习者不仅能够巩固之前的基础知识,还能掌握处理复杂数据结构和算法的能力。
本章实验的核心目标包括:
- 指针与数组的深度融合:理解指针算术运算与数组访问的等价性
- 字符串处理的实战技巧:掌握字符串函数库的使用和自定义字符串操作
- 结构体与共用体的应用:处理复杂数据类型和内存布局
- 文件I/O操作:实现数据的持久化存储和读取
- 动态内存管理:理解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;
}
调试技巧与性能优化
调试技巧
- 使用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");
}
- 内存检测工具
// 使用valgrind检测内存泄漏
// 编译时添加调试信息:gcc -g program.c -o program
// 运行:valgrind --leak-check=full ./program
性能优化建议
- 减少不必要的内存分配
// 不好的做法
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
}
- 批量操作优化
// 批量读取文件
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语言中级编程的核心内容,从指针与数组的深度结合到复杂的结构体管理,再到文件操作和动态内存管理。通过这些实验,学习者应该能够:
- 熟练掌握指针操作:理解指针与数组的关系,能够正确使用指针遍历和操作数组
- 精通字符串处理:掌握标准库函数和自定义字符串操作
- 熟练运用结构体:处理复杂数据类型,包括嵌套结构体和共用体
- 掌握文件I/O:实现数据的持久化存储和读取
- 正确管理内存:避免内存泄漏、悬空指针等常见问题
这些技能构成了C语言程序设计的坚实基础,为后续学习高级数据结构和系统编程打下了重要基础。在实际开发中,务必注意代码的健壮性和内存安全性,养成良好的编程习惯。
