C语言应用实例:学生管理系统1(指针、结构体综合应用,动态内存分配)

前面我们学习了指针和结构体,接下来我们以 学生管理系统 为例,综合运用一下它们

要求实现功能:增删查改

一、功能实现

1.菜单

为了方便使用,首先我们应当创建一个菜单函数,便于后续操作

cs 复制代码
void menu() {
	printf("Student Manage\n");
	printf("==[1001]添加学生==\n");
	printf("==[1002]查询所有学生==\n");
	printf("==[1003]根据ID查询学生==\n");
	printf("==[1004]根据ID修改信息==\n");
	printf("==[1005]根据ID删除信息==\n");
	printf("==[1006]退出系统==\n");
	printf("请输入功能编号:\n");
}

2.添加

接下来我们可以围绕上述功能分别定义函数来实现

首先是添加学生操作(addStu)

为了减少内存占用同时提高运行效率,这里我们采取动态分配的方式来分配内存

cs 复制代码
struct Student *addStu(Student *stulist, int *size, int *len) {
	//扩容判断
	int s = *size;
	int l = *len;
	if (s == l) {
		l = l * 2;
		struct Student *newStulist = (Student *)malloc(sizeof(Student) * l);
		for (int i = 0; i < s; i++) {
			newStulist[i] = stulist[i];
		}
		stulist = newStulist;
		printf("***扩容完成len=%d***\n", l);
	}
	printf("请输入\n姓名 学号 年龄 学分\n");
	char name[50];
	int id;
	int age;
	float score;
	scanf("%s %d %d %f", name, &id, &age, &score);
	//创建一个学生结构体变量
	Student stu;
	//把信息都存入学生结构体中
	strcpy(stu.name, name);
	stu.id = id;
	stu.age = age;
	stu.score = score;
	stulist[s] = stu;
	*size = s + 1;
	*len = l;
	printf("添加成功\n");

	return stulist;
}

注:这里需要注意,由于重新分配了内存,并返回了新结构体,所以主函数处应注意接收返回值,否则调用的仍是旧的内存地址,会出现乱码(如下)

cs 复制代码
 // 修复:接收ctrl函数的返回值
        struct Student *newStulist = ctrl(stulist, &size, &len);
        if (newStulist == 0) {  // 退出系统
            free(stulist);
            break;
        }
        stulist = newStulist;  // 更新指针

3.查找

1)查找所有

传递、调用,代码如下

cs 复制代码
void printAllStu(Student *stulist, int *size) {
	int s = *size;
	printf("======All Students======\n");//分割线
	for (int i = 0; i < s; i++) {
		struct Student stu = stulist[i];
		printf("--姓名:%s | 年龄:%d | 学号:%d | 学分:%.2f|--\n", stu.name, stu.age, stu.id, stu.score);
	}
}

2)ID查找

同理,代码如下

cs 复制代码
void getStuById(Student *stulist, int *size) {
	int s = *size;
	int id;
	printf("请输入一个学生ID:\n");
	scanf("%d", &id);
	for (int i = 0; i < s; i++) {
		struct Student stu = stulist[i];
		if (stu.id == id) {
			printf("ID存在,信息如下:\n");
			printf("--姓名:%s | 年龄:%d | 学号:%d | 学分:%.2f|--\n", stu.name, stu.age, stu.id, stu.score);
			return;
		}
	}
	printf("没有查找到ID为%d的学生!\n", id);
}

4.删除

同上,删除时只需将删除位置后的数往前挪动,覆盖住原数后size-1即可

代码如下

cs 复制代码
void delStuById(Student *stulist, int *size) {
	int s = *size;
	struct Student stu;
	int id;
	printf("请输入一个学生ID:\n");
	scanf("%d", &id);
	int index = -1;
	for (int i = 0; i < s; i++) {
		stu = stulist[i];
		if (stu.id == id) {
			index = i;

			break;
		}
	}
	if (index == -1) {
		printf("没有查找到ID为%d的学生\n", id);
		return;
	}
	for (int i = index; i < s - 1; i++) {
		stulist[i] = stulist[i + 1];
	}
	printf("ID存在,被删除的信息如下:\n");
	printf("--姓名:%s | 年龄:%d | 学号:%d | 学分:%.2f|--\n", stu.name, stu.age, stu.id, stu.score);
	*size = s - 1;
	printf("没有查找到ID为%d的学生!\n", id);
}

5.更改

循环、判断、修改、传递,代码如下

cs 复制代码
void changeInfoById(Student *stulist, int *size) {
	int s = *size;
	int id;
	printf("请输入一个学生ID:\n");
	scanf("%d", &id);
	for (int i = 0; i < s; i++) {
		struct Student stu = stulist[i];
		if (stu.id == id) {
			printf("ID存在,信息如下:\n");
			printf("--姓名:%s | 年龄:%d | 学号:%d | 学分:%.2f|--\n", stu.name, stu.age, stu.id, stu.score);
			printf("请输入新的信息:\n姓名 学号 年龄 学分\n");
			char name[50];
			int sid;
			int age;
			float score;
			scanf("%s %d %d %f", name, &sid, &age, &score);
			strcpy(stu.name, name);
			stu.id = sid;
			stu.age = age;
			stu.score = score;
			stulist[i] = stu;
			return;
		}
	}
	printf("没有查找到ID为%d的学生!\n", id);
}

二、完整代码

cs 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

void menu() {
	printf("Student Manage\n");
	printf("==[1001]添加学生==\n");
	printf("==[1002]查询所有学生==\n");
	printf("==[1003]根据ID查询学生==\n");
	printf("==[1004]根据ID修改信息==\n");
	printf("==[1005]根据ID删除信息==\n");
	printf("==[1006]退出系统==\n");
	printf("请输入功能编号:\n");
}
/*
//测试用例
void initTestData(Student *stulist, int *size, int *len){
	
	char str[5]={'a','b','c','d','e'};
	for(int i=0;i<100;i++){
		char name[50]="Name";
		name[4]=str[i%5];
	int id=i+100;
	int age=18+i%80;
	float score=12.5+i;
	scanf("%s %d %d %f", name, &id, &age, &score);
	//创建一个学生结构体变量
	Student stu;
	//把信息都存入学生结构体中
	strcpy(stu.name, name);
	stu.id = id;
	stu.age = age;
	stu.score = score;
	stulist[s] = stu;
	*size=s+1;
	}
	
}
*/

struct Student *addStu(Student *stulist, int *size, int *len) {
	//扩容判断
	int s = *size;
	int l = *len;
	if (s == l) {
		l = l * 2;
		struct Student *newStulist = (Student *)malloc(sizeof(Student) * l);
		for (int i = 0; i < s; i++) {
			newStulist[i] = stulist[i];
		}
		stulist = newStulist;
		printf("***扩容完成len=%d***\n", l);
	}
	printf("请输入\n姓名 学号 年龄 学分\n");
	char name[50];
	int id;
	int age;
	float score;
	scanf("%s %d %d %f", name, &id, &age, &score);
	//创建一个学生结构体变量
	Student stu;
	//把信息都存入学生结构体中
	strcpy(stu.name, name);
	stu.id = id;
	stu.age = age;
	stu.score = score;
	stulist[s] = stu;
	*size = s + 1;
	*len = l;
	printf("添加成功\n");

	return stulist;
}

//打印所有学生信息
void printAllStu(Student *stulist, int *size) {
	int s = *size;
	printf("======All Students======\n");
	for (int i = 0; i < s; i++) {
		struct Student stu = stulist[i];
		printf("--姓名:%s | 年龄:%d | 学号:%d | 学分:%.2f|--\n", stu.name, stu.age, stu.id, stu.score);
	}
	
}

//根据id查找学生信息
void getStuById(Student *stulist, int *size) {
	int s = *size;
	int id;
	printf("请输入一个学生ID:\n");
	scanf("%d", &id);
	for (int i = 0; i < s; i++) {
		struct Student stu = stulist[i];
		if (stu.id == id) {
			printf("ID存在,信息如下:\n");
			printf("--姓名:%s | 年龄:%d | 学号:%d | 学分:%.2f|--\n", stu.name, stu.age, stu.id, stu.score);
			return;
		}
	}
	printf("没有查找到ID为%d的学生!\n", id);
}

//根据id修改信息
void changeInfoById(Student *stulist, int *size) {
	int s = *size;
	int id;
	printf("请输入一个学生ID:\n");
	scanf("%d", &id);
	for (int i = 0; i < s; i++) {
		struct Student stu = stulist[i];
		if (stu.id == id) {
			printf("ID存在,信息如下:\n");
			printf("--姓名:%s | 年龄:%d | 学号:%d | 学分:%.2f|--\n", stu.name, stu.age, stu.id, stu.score);
			printf("请输入新的信息:\n姓名 学号 年龄 学分\n");
			char name[50];
			int sid;
			int age;
			float score;
			scanf("%s %d %d %f", name, &sid, &age, &score);
			strcpy(stu.name, name);
			stu.id = sid;
			stu.age = age;
			stu.score = score;
			stulist[i] = stu;
			return;
		}
	}
	printf("没有查找到ID为%d的学生!\n", id);
}

//根据ID删除学生信息
void delStuById(Student *stulist, int *size) {
	int s = *size;
	struct Student stu;
	int id;
	printf("请输入一个学生ID:\n");
	scanf("%d", &id);
	int index = -1;
	for (int i = 0; i < s; i++) {
		stu = stulist[i];
		if (stu.id == id) {
			index = i;

			break;
		}
	}
	if (index == -1) {
		printf("没有查找到ID为%d的学生\n", id);
		return;
	}
	for (int i = index; i < s - 1; i++) {
		stulist[i] = stulist[i + 1];
	}
	printf("ID存在,被删除的信息如下:\n");
	printf("--姓名:%s | 年龄:%d | 学号:%d | 学分:%.2f|--\n", stu.name, stu.age, stu.id, stu.score);
	*size = s - 1;
	printf("没有查找到ID为%d的学生!\n", id);
}


//控制面板
struct Student *ctrl(Student *stulist, int *size, int *len) {
	int cid = 0;
	scanf("%d", &cid);
	if (cid == 1001) {
		stulist = addStu(stulist, size, len);

	} else if (cid == 1002) {
		printAllStu(stulist, size);
	} else if (cid == 1003) {
		getStuById(stulist, size);
	} else if (cid == 1004) {
		changeInfoById(stulist, size);
	} else if (cid == 1005) {
		delStuById(stulist, size);
	} else if (cid == 1006) {
		free(stulist);
		return 0;
	} else {
		printf("输入有误,请重试!");
	}
	return stulist;
}


int main() {
	int len = 10;
	int size = 0;
	struct Student *stulist = (Student *)malloc(sizeof(Student) * len);

	while (1) {
		printf("===========begin=========\n");
		menu();
		// 修复:接收ctrl函数的返回值
		struct Student *newStulist = ctrl(stulist, &size, &len);
		if (newStulist == 0) {  // 退出系统
			free(stulist);
			break;
		}
		stulist = newStulist;  // 更新指针
		printf("===========End===========\n\n");

	}
	return 0;
}

三、常见问题

1.为什么要传递指针而不是数据本身?

函数内修改的是局部变量而不是原数据本身,如果想要修改原数据还需要在主函数中接收返回值,而传递地址(指针)则可以直接修改原数据,省去部分步骤

2.为什么数组不被定义就可以直接使用?

(以本文中stulist为例)

C语言中,指针可以使用数组下标语法:ptr[i]等价于*(ptr + i)

已知数组本身就是指针数据,即一个地址,所以指针变量在被分配内存后可以当作数组使用

相关推荐
小叮当⇔1 小时前
“征服式学习”提示词工具箱
学习·算法
惊讶的猫2 小时前
字符串- 字符串转换整数 (atoi)
数据结构·算法
开心-开心急了2 小时前
关于Flutter与Qt for python 的一些技术、开源、商用等问题
开发语言·python·qt·flutter
友友马2 小时前
『 QT 』按钮类控件属性解析
开发语言·数据库·qt
Evand J2 小时前
【MATLAB例程】基于噪声协方差自适应的互补滤波器方法vs标准互补滤波,用于融合加速度计和陀螺仪数据,估计角度
开发语言·matlab
熊小猿2 小时前
RabbitMQ死信交换机与延迟队列:原理、实现与最佳实践
开发语言·后端·ruby
Mark_Hide2 小时前
学习笔记7
笔记·学习
@小码农2 小时前
2025年北京海淀区中小学生信息学竞赛第一赛段试题(附答案)
人工智能·python·算法·蓝桥杯
2301_795167202 小时前
玩转Rust高级应用 如何让让运算符支持自定义类型,通过运算符重载的方式是针对自定义类型吗?
开发语言·后端·算法·安全·rust