1.首先是头文件:
cpp
//student.h
//头文件
//防止头文件被重复包含
#pragma once
//宏定义符号常量,方便维护和修改
#define ID_MAX 20
#define NAME_MAX 20
#define AGE_MAX 5
#define SEX_MAX 5
#define CLA_MAX 20
//定义初始最大容量
#define MAX 1
//定义结构体学生
struct Student
{
//定义学生信息
char id[ID_MAX];
char name[NAME_MAX];
char age[AGE_MAX];
char sex[SEX_MAX];
char cla[CLA_MAX];
};
//定义结构体学生信息本
struct Book
{
//数据
struct Student* data;
//当前学生个数
int sz;
//当前容量
int capacity;
};
//项目函数声明
void menu();
void InitBook(struct Book* stu);
void ReadBook(struct Book* stu);
void WriteBook(struct Book* stu);
void CheckBook(struct Book* stu);
void clear_screen();
void AddBook(struct Book* stu);
void ShowBook(struct Book* stu);
void CheckCapacity(struct Book* stu);
void ExitBook(struct Book* stu);
void ClearBook(struct Book* stu);
2. 然后是功能函数student.c文件
cpp
//student.c
//函数体文件
//调用头文件
#include "student.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//定义函数体
//定义清屏函数
//清屏操作
void clear_screen() {
//判断是否为Windows系统
#ifdef _WIN32
system("cls");
//其他系统
#else
system("clear");
#endif
}
//菜单函数
void menu()
{
printf("*********************************************\n");
printf("******** 1.添加 2.删除 **************\n");
printf("******** 3.查询 4.修改 **************\n");
printf("******** 5.查看 6.排序 **************\n");
printf("******** 7.清空 0.退出 **************\n");
printf("*********************************************\n");
}
//检查容量是否溢出
void CheckCapacity(struct Book* stu) {
if (stu->sz == 0) {
printf("当前学生信息本为空!\n");
}
printf("当前容量为:%d\n", stu->capacity);
printf("当前学生为:%d\n", stu->sz);
}
//检查容量函数
void CheckBook(struct Book* stu) {
//检查是否溢出
if (stu->sz == stu->capacity) {
printf("容量已满!开始扩容!\n");
int newcapacity = (stu->capacity == 0) ? MAX : stu->capacity * 2;
struct Student* cap = (struct Student*)realloc(stu->data, newcapacity * sizeof(struct Student));
if (cap == NULL) {
printf("扩容失败!\n");
return;
}
stu->data = cap;
stu->capacity = newcapacity;
printf("扩容成功!\n");
}
}
//初始化学生信息函数
void InitBook(struct Book* stu) {
//初始化为0或空
stu->sz = 0;
stu->data = NULL;
stu->capacity = 0;
//读取文件信息
ReadBook(stu);
//如果文件没有数据,初始化空间内存
if (stu->sz == 0) {
stu->data = (struct Student*)calloc(MAX, sizeof(struct Student));
if (stu->data == NULL) {
printf("初始化空间内存失败!\n");
return;
}
stu->capacity = MAX;
}
}
//读取文件函数
void ReadBook(struct Book* stu) {
//打开文件
FILE* fp = fopen("Studentbook.txt", "rb");
if (fp == NULL) {
return;
}
//定义一个临时结构体
struct Student tmp;
while (fread(&tmp,sizeof(struct Student),1,fp)) {
//检查容量是否溢出
CheckBook(stu);
stu->data[stu->sz] = tmp;
stu->sz++;
CheckCapacity(stu);
}
fclose(fp);
fp = NULL;
}
//写入文件
void WriteBook(struct Book* stu) {
FILE* fp = fopen("Studentbook.txt", "wb");
if (fp == NULL) {
printf("读取文件失败!\n");
return;
}
for (int i = 0; i < stu->sz; i++) {
fwrite((stu->data + i), sizeof(struct Student), 1, fp);
}
fclose(fp);
fp = NULL;
}
//添加学生信息
void AddBook(struct Book* stu) {
CheckBook(stu);
printf("请输入学号:");
scanf("%s", stu->data[stu->sz].id);
printf("请输入姓名:");
scanf("%s", stu->data[stu->sz].name);
printf("请输入年龄:");
scanf("%s", stu->data[stu->sz].age);
printf("请输入性别:");
scanf("%s", stu->data[stu->sz].sex);
printf("请输入班级:");
scanf("%s", stu->data[stu->sz].cla);
printf("添加成功!\n");
(stu->sz)++;
CheckCapacity(stu);
}
//查询学生信息本
void ShowBook(struct Book* stu) {
CheckCapacity(stu);
printf("%-19s\t%-15s\t%-5s\t%-8s\t%-30s\n", "学号","姓名", "年龄", "性别", "班级");
for (int i = 0; i < stu->sz; i++) {
printf("%-19s\t%-15s\t%-5s\t%-8s\t%-30s\n", stu->data[i].id,stu->data[i].name,
stu->data[i].age, stu->data[i].sex, stu->data[i].cla);
}
}
//退出学生信息本函数
void ExitBook(struct Book* stu) {
WriteBook(stu);
printf("退出成功!欢迎下次使用!\n");
}
//释放空间函数
void ClearBook(struct Book* stu) {
free(stu->data);
stu->data = NULL;
stu->sz = 0;
stu->capacity = 0;
}
3.最后是主程序test.c文件:
cpp
//test.c
//测试文件
//调用头文件
#include "student.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum Option
{
EXIT,//0,对应退出通讯录
ADD,//1,对应添加联系人
DEL,//2,对应删除联系人
SEARCH,//3,对应查询联系人
MODIFY,//4,对应修改联系人
SHOW,//5,对应查看通讯录
SORT,//6,对应排序通讯录
CLEAR,//7,对应清空通讯录
};
//程序主函数
int main() {
//定义结构体变量
struct Book stu;
//初始化学生信息
InitBook(&stu);
int input = 0;
int menu_0 = 0;
do {
//打印菜单
while (1) {
printf("************按1继续************\n");
if (scanf("%d", &menu_0) != 1 && menu_0 != 1) {
printf("输入不合法,请按1继续\n");
return 1;
}
clear_screen();
if (menu_0 == 1)
{
menu();
break;
}
}
printf("请选择对应模式(0-7):\n");
if (scanf("%d", &input) != 1 || input < 0 || input > 7) {
printf("输入不合法,请输入整数0-7\n");
return 1;
}
switch (input)
{
case ADD: {
clear_screen();
AddBook(&stu);
break;
}
case SHOW: {
clear_screen();
ShowBook(&stu);
break;
}
case EXIT: {
clear_screen();
ExitBook(&stu);
break;
}
default:
break;
}
} while (input);
ClearBook(&stu);
return 0;
}
整个项目只有三个文件,头文件和两个源代码
一、头文件
student.h
1. 防止头文件重复包含
#pragma once
- 作用 :确保头文件在单个编译单元中只被包含一次,替代传统的
#ifndef
宏定义方式。2. 宏定义常量
#define ID_MAX 20 #define NAME_MAX 20 // ...
作用:统一管理字符串长度限制,提高代码可维护性。
示例 :
char id[ID_MAX]
表示学号最多存储 19 个字符 + 1 个终止符\0
。3. 结构体定义
struct Student { char id[ID_MAX]; char name[NAME_MAX]; // ... };
知识点:结构体用于封装学生信息字段,每个字段是定长字符数组。
内存布局 :
Student
结构体的大小为ID_MAX + NAME_MAX + ...
字节。
struct Book { struct Student* data; int sz; int capacity; };
- 作用 :管理动态数组,
data
指向堆内存,sz
为当前学生数,capacity
为容量。4. 函数声明
void InitBook(struct Book* stu); void AddBook(struct Book* stu); // ...
- 作用:声明函数原型,实现模块化编程。
二、核心功能文件
student.c
1. 清屏函数
void clear_screen() { #ifdef _WIN32 system("cls"); #else system("clear"); #endif }
- 跨平台处理:通过预编译指令区分 Windows 和其他系统(如 Linux/macOS)。
2. 菜单函数
void menu() { printf("*********************************************\n"); printf("******** 1.添加 2.删除 **************\n"); // ... }
- 作用:打印用户操作菜单,通过数字选择功能。
3. 动态内存管理
void CheckBook(struct Book* stu) { if (stu->sz == stu->capacity) { int newcapacity = (stu->capacity == 0) ? MAX : stu->capacity * 2; struct Student* cap = realloc(stu->data, newcapacity * sizeof(struct Student)); // ... } }
知识点:
realloc
:动态调整内存大小,初始容量为MAX=1
,后续每次扩容为当前容量的 2 倍。扩容策略:避免频繁扩容,时间复杂度均摊为 O(1)。
风险点 :若
realloc
失败需处理NULL
返回值。4. 文件读写
void ReadBook(struct Book* stu) { FILE* fp = fopen("Studentbook.txt", "rb"); while (fread(&tmp, sizeof(struct Student), 1, fp)) { CheckBook(stu); // 动态扩容 stu->data[stu->sz++] = tmp; } }
知识点:
fopen
:以二进制读模式打开文件,"rb"
表示读取二进制数据。
fread
:按块读取数据,每次读取一个Student
结构体。文件格式:数据以二进制形式存储,与内存布局一致。
void WriteBook(struct Book* stu) { FILE* fp = fopen("Studentbook.txt", "wb"); for (int i = 0; i < stu->sz; i++) { fwrite(&stu->data[i], sizeof(struct Student), 1, fp); } }
- 知识点 :
fwrite
将内存中的结构体直接写入文件,效率高但缺乏可读性。5. 添加学生
void AddBook(struct Book* stu) { CheckBook(stu); // 确保容量足够 scanf("%s", stu->data[stu->sz].id); // 输入学号 // ...其他字段输入 stu->sz++; // 更新学生数量 }
风险点 :
scanf
未限制输入长度,可能溢出缓冲区。改进建议 :使用
scanf("%19s", id)
限制输入长度。6. 显示学生信息
void ShowBook(struct Book* stu) { printf("%-19s\t%-15s\t...\n", "学号", "姓名", ...); for (int i = 0; i < stu->sz; i++) { printf("%-19s\t%-15s\t...\n", stu->data[i].id, stu->data[i].name, ...); } }
- 格式化输出 :
%-19s
表示左对齐且占 19 字符宽度,确保表格对齐。
三、主程序
test.c
1. 枚举定义
enum Option { EXIT, ADD, DEL, SEARCH, MODIFY, SHOW, SORT, CLEAR };
- 作用:用枚举常量代替数字,提高代码可读性。
2. 主函数逻辑
int main() { struct Book stu; InitBook(&stu); // 初始化 do { menu(); // 显示菜单 scanf("%d", &input); switch(input) { case ADD: AddBook(&stu); break; // ...其他选项 } } while (input != EXIT); ClearBook(&stu); // 释放内存 return 0; }
知识点:
do-while
循环:确保至少执行一次菜单显示。内存泄漏预防 :退出时调用
ClearBook
释放堆内存。
四、关键知识点总结
1. 动态内存管理
函数 :
calloc
、realloc
、free
应用场景:学生数据的动态扩容和释放。
示例:
struct Student* cap = realloc(old_ptr, new_size); if (cap == NULL) { /* 处理失败 */ }
2. 文件操作
模式 :
"rb"
(二进制读)、"wb"
(二进制写)函数 :
fopen
、fread
、fwrite
、fclose
注意事项:二进制文件无换行符,直接读写结构体。
3. 结构体与内存布局
对齐:结构体成员在内存中按声明顺序连续存储,可能存在填充字节。
示例 :
Student
结构体的内存布局直接影响文件读写。4. 输入输出安全
风险 :
scanf
不限制输入长度,可能导致缓冲区溢出。改进:
char id[ID_MAX]; scanf("%19s", id); // 限制输入长度为 ID_MAX-1
五、操作示例
1. 添加学生
请输入学号:2023001 请输入姓名:张三 请输入年龄:20 请输入性别:男 请输入班级:计算机1班 添加成功!
2. 查看学生
学号 姓名 年龄 性别 班级 2023001 张三 20 男 计算机1班
3. 退出程序
- 数据自动保存到
Studentbook.txt
,下次启动自动加载。
六、扩展与改进建议
1. 输入验证
// 示例:安全输入年龄 char age[AGE_MAX]; if (scanf("%4s", age) != 1 || !is_numeric(age)) { printf("年龄不合法!\n"); }
2. 错误处理
FILE* fp = fopen("studentbook.txt", "rb"); if (fp == NULL) { perror("错误原因"); // 输出系统错误信息 return; }
3. 功能扩展
删除学生:实现通过学号或姓名删除。
修改学生:根据学号定位学生并修改字段。
排序学生 :按学号或姓名排序(可使用
qsort
函数)。
cpp
容量已满!开始扩容!
扩容成功!
当前容量为:1
当前学生为:1
容量已满!开始扩容!
扩容成功!
当前容量为:2
当前学生为:2
容量已满!开始扩容!
扩容成功!
当前容量为:4
当前学生为:3
当前容量为:4
当前学生为:4
************按1继续************
cpp
当前容量为:4
当前学生为:4
学号 姓名 年龄 性别 班级
231 张三 12 男 C语言2班
232 李四 13 女 C语言3班
456 翟六 34 女 C语言9班
233 wangwu 12 2 22-2
************按1继续************
源代码:
注:该代码是本人自己所写,可能不够好,不够简便,欢迎大家指出我的不足之处。如果遇见看不懂的地方,可以在评论区打出来,进行讨论,或者联系我。上述内容全是我自己理解的,如果你有别的想法,或者认为我的理解不对,欢迎指出!!!如果可以,可以点一个免费的赞支持一下吗?谢谢各位彦祖亦菲!!!!!