(C语言)学生信息表(学生管理系统)(基于通讯录改版)(正式版)(C语言项目)

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. 动态内存管理
  • 函数callocreallocfree

  • 应用场景:学生数据的动态扩容和释放。

  • 示例

    复制代码
    struct Student* cap = realloc(old_ptr, new_size);
    if (cap == NULL) { /* 处理失败 */ }
2. 文件操作
  • 模式"rb"(二进制读)、"wb"(二进制写)

  • 函数fopenfreadfwritefclose

  • 注意事项:二进制文件无换行符,直接读写结构体。

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继续************

源代码:

双叶/学生管理系统

注:该代码是本人自己所写,可能不够好,不够简便,欢迎大家指出我的不足之处。如果遇见看不懂的地方,可以在评论区打出来,进行讨论,或者联系我。上述内容全是我自己理解的,如果你有别的想法,或者认为我的理解不对,欢迎指出!!!如果可以,可以点一个免费的赞支持一下吗?谢谢各位彦祖亦菲!!!!!

相关推荐
qq_5298353519 分钟前
装饰器模式:如何用Java打扮一个对象?
java·开发语言·装饰器模式
日暮南城故里23 分钟前
Java学习------源码解析之StringBuilder
java·开发语言·学习·源码
Vitalia1 小时前
从零开始学Rust:枚举(enum)与模式匹配核心机制
开发语言·后端·rust
双叶8361 小时前
(C语言)虚数运算(结构体教程)(指针解法)(C语言教程)
c语言·开发语言·数据结构·c++·算法·microsoft
工一木子1 小时前
大厂算法面试 7 天冲刺:第5天- 递归与动态规划深度解析 - 高频面试算法 & Java 实战
算法·面试·动态规划
一个public的class3 小时前
什么是 Java 泛型
java·开发语言·后端
士别三日&&当刮目相看3 小时前
JAVA学习*Object类
java·开发语言·学习
invincible_Tang3 小时前
R格式 (15届B) 高精度
开发语言·算法·r语言
一只小松许️3 小时前
Rust闭包详解
开发语言·rust
独好紫罗兰4 小时前
洛谷题单2-P5715 【深基3.例8】三位数排序-python-流程图重构
开发语言·python·算法