草庐IT

赶紧进来看看---C语言实现学生信息管理系统(2.0动态内存版)

牛牛要坚持 2023-04-08 原文

本文介绍了将学生信息管理系统静态版本改造为动态内存版本,主要涉及改造思路,枚举类型的使用,动态内存函数和柔性数组的使用,动手写程序才能使基础知识更为牢固…(文章最后有源码展示)

学生信息管理系统1.0静态版->学生信息管理系统2.0动态内存版

c语言实现学生信息管理系统---2.0动态内存版

一.学生信息管理系统—静态版本的特点

在之前博客中用c语言实现了静态版本的学生信息管理系统,
所谓静态就是能够容纳学生信息个数的最大容量编写代码时就已经确定,即编译代码运行程序时会根据所编写的代码一次性申请开辟对应的空间 ,
这种空间开辟的方法特点:开辟空间快捷方便,编写代码过程简洁,执行效率高

二.设计学生信息管理系统–动态内存版本

下面对静态版本的缺点进行改造的动态内存版设计介绍

1.为什么要设计动态内存版本

静态版本虽然方便,但是,什么东西都不会是一尘不变的,
实际情况千变万化,静态版本虽然简单快捷执行效率高,但是也有很多致命弊端如:
1.一次开辟的空间大小是固定的无法改变
2.无法确定程序运行过程中实际具体需要多大的空间
3.开始设定的容纳学生信息空间太小在实际运行中可能存放学生信息多会造成溢出,此时多出来的学生信息将无法存储!!!
4.开始设定的容纳学生信息空间太大,在实际运行中存放学生信息数量少时,所开辟的空间剩余太多的空余的空间被浪费掉…
而空间是有限在资源,静态空间满足不了实际程序需求
因此我们需要设计一种以时间换空间的方法即动态内存增长版来解决遇到的问题…

2.动态内存版本设计思路

动态内存版本设计主要解决静态版本遇到的问题…
1.首先最大容量一开始可以设定小一点,可以避免学生信息一开始数量少可以节省更多空间
2.设定一个增长容量数SZ,每次容量满的时候自动往后增加SZ个空间,以供后面继续添加学生信息
3.动态内存版本需要内存空间能够动态变化,空间需要在堆区上申请开辟,需要用到动态内存函数…

三.设计学生信息管理系统–动态内存版

下面是对静态版本改造为动态版本的过程…(主要用到了动态内存增长函数 和柔性数组的使用具体知识点在这篇博客中介绍到 ->动态内存管理)

1.用枚举类型改造功能选项

在这篇博客->自定义类型:枚举和联合体类型 中讲到了枚举类型,在改造静态版本时应用以下枚举类型…

在上面图中可以看到在switch case的分支中我们使用了数字0-7对应着不同的功能,但是在实际开始编写过程中,数字表达的含义是我们自己赋予的,那这样还不如在编写前设定枚举类型,将所有对应的数字选项都替换成枚举常量来表示…

enum option //声明 枚举类型 option 对应的枚举常量为实现 管理系统 菜单功能的选项名称  枚举常量不能同名
{
	EXIT,  //退出
	ADD,   //添加
	DROP,   //删除
	FIND,   //查找
	MODIFY, //修改
	TOTAL, //统计
	PRINT, //打印
	SORT,  //排序

};
enum option1   // 修改菜单的选择项
{
	END,   //结束修改
	MODALL, //修改整条信息
	MODONE  //修改某一项信息
};
enum option2  //修改单项的菜单选择项
{
	RETURN,   //返回上一层
	MODSNO, //修改学号
	MODNAME, //修改姓名
	MODSEX,  //修改性别
	MODAGE, //修改年龄
	MODROOM, //修改寝室号
	MODNUMBER, //修改电话号码
	MODADDRESS //修改地址
};
enum option3 //统计学生个数菜单 选择项
{
	ENDCOUNT,  //结束统计
	COUNTALL,  //统计所有学生信息
	COUNTAGE, // 按年龄统计
	COUNTROOM, //统计寝室号
	COUNTSEX //按性别统计
};



上面代码在头文件stu.h里根据要选择的对应选项设计了多个枚举类型,每个枚举类型里的枚举常量对应着选项数字的功能,这样在设计选项对应选项功能时能更直观的表达对应的功能有对应类型检查后更清晰直观后续调试除错增加对应类型的功能时在对应枚举类型里添加对应枚举常量即可…

2.设计动态内存结构

根据上面的动态内存设计思路, 最大容量应该是能动态变化的,而每次容量满时增长的大小是固定不变的.

对存放学生信息的结构体进行改造↓

改造后↓

typedef struct studentmanagement
{
	
	int sz;       //记录当前有效的学生信息个数
	int capcity;   //记录存放学生信息的最大容量
	studata stu[]; //柔性数组 用于不确定数组元素个数 实现动态增长
}stumgt;

增加了一个 capcity 表示存放学生信息的最大容量,将stu[100]固定的100个大小改为了柔性数组的形式stu[]放在的最后

设计初始的容量大小和每次增容的大小↓

#define SZ 6         //每次增加的容量个数
#define CAPCITY 3    //一开始设置的容量

用#define宏定义 设定 SZ为6 表示每次增容的个数 CAPCITY为3 表示初始时容量的大小
用宏定义 在后续想更改初始化容量或者增容大小可以直接更改对应宏后面的常量数可维护性更高

3.开辟动态内存空间和初始化

开辟静态空间的方式(直接在栈区申请固定空间)↓

stumgt stus;

改造后开辟动态内存方式↓

	stumgt *stus=(stumgt *)calloc(1,sizeof(stumgt)+sizeof(studata)*CAPCITY);//在堆区创建stumgt类型大小的空间给柔性数组分配的空间 

在堆区申请空间用到calloc动态内存开辟函数,表示开辟1个 sizeof(stumgt)+sizeof(studata)*CAPCITY)大小的空间,并且将空间都初始化为0…
根据柔性数组的使用 sizeof(stumgt) 表示的是非柔性数组以外的空间大小 sizeof(studata)*CAPCITY)表示柔性数组内部的大小,即CAPCITY个学生信息容量大小

改造后结构传参时直接传指针变量stus即可

初始化学生信息管理结构体变量改造前↓

void initstus(stumgt* pstus)//初始化学生管理系统这个结果体变量
{
	assert(pstus != NULL);
	memset(pstus, 0, sizeof(stumgt));
}

初始化学生信息管理结构体变量改造后↓

void initstus(stumgt* pstus)//初始化学生管理系统这个结构体变量
{
	assert(pstus != NULL);
	pstus->capcity = CAPCITY;        //将最大容量设置为初始化容量大小
}

改造后 只需学生信息管理结构体中的最大容量成员变量初始化为 设置的初始化容量大小

4.设计动态增容自定义函数

在初始化的容量满后需要扩容,此时封装一个扩容函数实现对动态内存增容

函数定义↓

addcapcity(&pstus);

函数定义↓

static void addcapcity(stumgt** pstus)
{
	stumgt* tmp=(stumgt*)realloc(*pstus, sizeof(stumgt) + sizeof(studata) * ((*pstus)->capcity + SZ));    //在堆区创建stumgt类型大小的空间给柔性数组分配的空间 
	if (tmp == NULL)    // 增容失败 显示错误并返回
	{
		perror("realloc");
		return;
	}
	else
	{
		*pstus = tmp;     // 将重新开辟的空间地址赋给main函数里的pstus指针变量
		(* pstus)->capcity += SZ;             //最大容量增容 SZ 个
	}
}

注意:动态增容函数的实参传参需要是二级指针,因为 realloc重置动态空间函数使用时可能在堆区另外一块区域重新开辟空间返回重新开辟的空间指针,而需要将该指针传stus指针变量里则需要二级指针才能实现,否则只能对形参临时开辟的局部变量赋值…

如果增容失败会返回空指针 经过判断后显示增容失败信息然后返回

增容操作每次增加当前最大容量加SZ个大小后将最大容量增加SZ个供下次继续增容

5.改造增加学生信息自定义函数

有了增容函数后,增加学生信息就不再仅限于最后不能超过固定的学生个数了

改造添加学生信息函数前↓

void addstu(stumgt* pstus)
{
	assert(pstus != NULL);
	if (pstus->sz == 100)
	{
		printf("学生信息已满,无法新增学生信息\n");
		return;
	}
	else
	{
		system("cls"); //输入学生信息前清屏一次
		printf("请输入学生学号:");
		char tmp[20] = { 0 };
		scanf("%s", tmp);
		if (is_repeat(pstus, tmp))   //学生学号为主要属性  应是唯一的且不能为空
		{
			printf("输入失败!!!已存在学号信息为%s的学生,不同学生信息不允许设置相同学号\n", tmp);
			return;
		}

		strcpy(pstus->stu[pstus->sz].sno, tmp);  //不存在重复时将当前信息拷贝作为学生学号信息
		printf("请输入学生名字:");
		scanf("%s", pstus->stu[pstus->sz].name);
		printf("请输入学生性别:");
		scanf("%s", pstus->stu[pstus->sz].sex);
		printf("请输入学生年龄:");
		scanf("%d", &pstus->stu[pstus->sz].age); //注意年龄这里要&其他的都是地址
		printf("请输入学生寝室号:");
		scanf("%s", pstus->stu[pstus->sz].roomnum);
		printf("请输入学生电话号码:");
		scanf("%s", pstus->stu[pstus->sz].number);
		printf("请输入学生家庭住址:");
		scanf("%s", pstus->stu[pstus->sz].add);
		pstus->sz++;
		system("cls");
		printf("添加成功\n");
	}
}

改造添加学生信息函数后↓

void addstu(stumgt* pstus)
{
	assert(pstus != NULL);
	if (pstus->sz ==pstus-> capcity)             // 当sz有效个数等于最大容量时进入分支
	{
		addcapcity(&pstus);                 // 调用增容函数
		printf("存储空间已满..增容中...\n");   // 制造增容效果
		Sleep(3000);                            //睡眠3秒
		
	}
	if (pstus->sz == pstus->capcity)          //  当调用增容后 有效个数还是等于最大容量时则表示增容失败了 此时进入该分支  否则则增容成功 进行后续添加学生信息操作
	{
		printf("容量已满,无法再添加学生信息\n");
		return;

	}
	else
	{
		system("cls"); //输入学生信息前清屏一次
		printf("请输入学生学号:");
		char tmp[20] = { 0 };
		scanf("%s", tmp);
		if (is_repeat(pstus, tmp))   //学生学号为主要属性  应是唯一的且不能为空
		{
			printf("输入失败!!!已存在学号信息为%s的学生,不同学生信息不允许设置相同学号\n", tmp);
			return;
		}

		strcpy(pstus->stu[pstus->sz].sno, tmp);  //不存在重复时将当前信息拷贝作为学生学号信息
		printf("请输入学生名字:");
		scanf("%s", pstus->stu[pstus->sz].name);
		printf("请输入学生性别:");
		scanf("%s", pstus->stu[pstus->sz].sex);
		printf("请输入学生年龄:");
		scanf("%d", &pstus->stu[pstus->sz].age); //注意年龄这里要&其他的都是地址
		printf("请输入学生寝室号:");
		scanf("%s", pstus->stu[pstus->sz].roomnum);
		printf("请输入学生电话号码:");
		scanf("%s", pstus->stu[pstus->sz].number);
		printf("请输入学生家庭住址:");
		scanf("%s", pstus->stu[pstus->sz].add);
		pstus->sz++;
		system("cls");
		printf("添加成功\n");
	}
}

经过改造后,学生信息容量不局限于是100个而是可以动态增容…

6.改造排序学生信息自定义函数

排序学生信息函数改造前↓

void sortstu(stumgt* pstus) //  升序排序所有学生记录的函数
{
	if (pstus->sz == 0)   //判断排序时是否为空
	{
		printf("当前学生记录为空,无法排序\n");
		return;
	}
	qsort(pstus, pstus->sz, sizeof(studata), cmp_sno);
	printf("已按学号完成升序排序\n");
}

排序学生信息函数改造后↓

void sortstu(stumgt* pstus) //  升序排序所有学生记录的函数
{
	if (pstus->sz == 0)   //判断排序时是否为空
	{
		printf("当前学生记录为空,无法排序\n");
		return;
	}
	qsort(pstus->stu, pstus->sz, sizeof(studata), cmp_sno);    //
	printf("已按学号完成升序排序\n");
}

静态版本学生信息管理结构体成员变量位置不同,排序是对学生信息数组进行排序,所以要将第一个元素改为指向学生数组.

四.学生信息管理系统(动态内存版)源码展示

1.test.c源文件

#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable : 6031)
#include"stu.h"


int main()
{
	stumgt *stus=(stumgt *)calloc(1,sizeof(stumgt)+sizeof(studata)*CAPCITY);//在堆区创建stumgt类型大小的空间给柔性数组分配的空间 

	int i = 0;
	initstus(stus);
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &i);
		switch (i)
		{
		case ADD:
			addstu(&stus);  // 因为要动态增容变量里的值可能要改变 此时取当前main里的stus变量地址传参过去
			break;
		case DROP:
			dropstu(stus);
			break;
		case FIND:
			search(stus);
			break;
		case MODIFY:
			modifystu(stus);
			break;
		case TOTAL:
			countstu(stus);
			break;
		case PRINT:
			pritstus(stus);
			break;
		case SORT:
			sortstu(stus);
			break;
		case EXIT:
			printf("已退出学生信息管理系统\n");
			break;
		default:
			printf("选择错误,请重新输入\n");
			break;
		}
	} while (i);
	return 0;
}

2.stu.h头文件

#pragma once
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
#include<windows.h>
#define SZ 6         //每次增加的容量个数
#define CAPCITY 3    //一开始设置的容量
typedef struct studentdata
{
	char sno[20];  //学号
	char name[20]; //名字
	char sex[5]; //性别
	int age;  //年龄
	char roomnum[20];// 寝室号
	char number[12];// 电话号码
	char add[20];  //地址
}studata;

typedef struct studentmanagement
{
	
	int sz;       //记录当前有效的学生信息个数
	int capcity;   //记录存放学生信息的最大容量
	studata stu[]; //柔性数组 用于不确定数组元素个数 实现动态增长
}stumgt;
enum option //声明 枚举类型 option 对应的枚举常量为实现 管理系统 菜单功能的选项名称  枚举常量不能同名
{
	EXIT,  //退出
	ADD,   //添加
	DROP,   //删除
	FIND,   //查找
	MODIFY, //修改
	TOTAL, //统计
	PRINT, //打印
	SORT,  //排序

};
enum option1   // 修改菜单的选择项
{
	END,   //结束修改
	MODALL, //修改整条信息
	MODONE  //修改某一项信息
};
enum option2  //修改单项的菜单选择项
{
	RETURN,   //返回上一层
	MODSNO, //修改学号
	MODNAME, //修改姓名
	MODSEX,  //修改性别
	MODAGE, //修改年龄
	MODROOM, //修改寝室号
	MODNUMBER, //修改电话号码
	MODADDRESS //修改地址
};
enum option3 //统计学生个数菜单 选择项
{
	ENDCOUNT,  //结束统计
	COUNTALL,  //统计所有学生信息
	COUNTAGE, // 按年龄统计
	COUNTROOM, //统计寝室号
	COUNTSEX //按性别统计
};
void menu();
//void initstus(stumgt* pstus);
void addstu(stumgt** pstus);
void pritstus(stumgt* pstus);
void dropstu(stumgt* pstus);
void search(stumgt* pstus);
void modifystu(stumgt* pstus);
void countstu(stumgt* pstus);
void sortstu(stumgt* pstus);

3.stu.c源文件

#define _CRT_SECURE_NO_WARNINGS  
#pragma warning(disable : 6031) //忽略返回值
#include"stu.h"

void menu()
{
	printf("           欢迎使用学生信息管理系统              \n");
	printf("*************************************************\n");
	printf("#  1.添加新的学生信息    2.删除指定学生信息     #\n");
	printf("#  3.查找指定学生信息    4.修改指定学生信息     #\n");
	printf("#  5.分类统计学生个数    6.显示所有学生信息     #\n");
	printf("#  7.排序所有学生信息    0.退出学生管理系统     #\n");
	printf("*************************************************\n");
}
static void modifymenu1()
{
	printf("*************************************************\n");
	printf("#  1.修改整条学生信息    2.修改某项学生信息     #\n");
	printf("#               0.退出修改菜单                  #\n");
	printf("*************************************************\n");
}

static void modifymenu2()
{
	printf("*************************************************\n");
	printf("#       1.修改学生学号    2.修改学生姓名        #\n");
	printf("#       3.修改学生性别    4.修改学生年龄        #\n");
	printf("#       5,修改学生寝室号  6.修改学生电话号码    #\n");
	printf("#       7.修改学生家庭住址 0.返回上一步修改     #\n");
	printf("*************************************************\n");
}
static void countmenu()
{
	printf("*************************************************\n");
	printf("#  1.统计所有学生个数    2.按年龄统计学生个数   #\n");
	printf("#  3.按寝室号统计学生个数4.按性别统计学生个数   #\n");
	printf("#                  0.结束统计                   #\n");
	printf("*************************************************\n");
}

void initstus(stumgt* pstus)//初始化学生管理系统这个结果体变量
{
	assert(pstus != NULL);
	pstus->capcity = CAPCITY;        //  //将最大容量设置为初始化容量大小
}
static void addcapcity(stumgt** pstus)
{
	stumgt* tmp=(stumgt*)realloc(*pstus, sizeof(stumgt) + sizeof(studata) * ((*pstus)->capcity + SZ));    //在堆区创建stumgt类型大小的空间给柔性数组分配的空间 
	if (tmp == NULL)    // 增容失败 显示错误并返回
	{
		perror("realloc");
		return;
	}
	else
	{
		*pstus = tmp;     //
		(* pstus)->capcity += SZ;             //最大容量增容 SZ 个
	}
	

}
static int is_repeat(stumgt* pstus, char tmp[]) //判断学号信息是否重复
{
	int i = 0;
	for (i = 0; i < pstus->sz; i++)
	{
		if (strcmp(tmp, pstus->stu[i].sno) == 0)
		{
			return 1;
		}
	}
	return 0;
}
void addstu(stumgt** pstus)
{
	assert(*pstus != NULL);
	if (( * pstus)->sz == (*pstus)->capcity)             // 当sz有效个数等于最大容量时进入分支
	{
		addcapcity(pstus);                 // 调用增容函数
		printf("存储空间已满..增容中...\n");   // 制造增容效果
		Sleep(3000);                            //睡眠3秒
		
	}
	if ((*pstus)->sz == (*pstus)->capcity)          //  当调用增容后 有效个数还是等于最大容量时则表示增容失败了 此时进入该分支  否则则增容成功 进行后续添加学生信息操作
	{
		printf("容量已满,无法再添加学生信息\n");
		return;

	}
	else
	{
		system("cls"); //输入学生信息前清屏一次
		printf("请输入学生学号:");
		char tmp[20] = { 0 };
		scanf("%s", tmp);
		if (is_repeat(*pstus, tmp))   //学生学号为主要属性  应是唯一的且不能为空
		{
			printf("输入失败!!!已存在学号信息为%s的学生,不同学生信息不允许设置相同学号\n", tmp);
			return;
		}

		strcpy((*pstus)->stu[(*pstus)->sz].sno, tmp);  //不存在重复时将当前信息拷贝作为学生学号信息
		printf("请输入学生名字:");
		scanf("%s", (*pstus)->stu[(*pstus)->sz].name);
		printf("请输入学生性别:");
		scanf("%s", (*pstus)->stu[(*pstus)->sz].sex);
		printf("请输入学生年龄:");
		scanf("%d", &(*pstus)->stu[(*pstus)->sz].age); //注意年龄这里要&其他的都是地址
		printf("请输入学生寝室号:");
		scanf("%s", (*pstus)->stu[(*pstus)->sz].roomnum);
		printf("请输入学生电话号码:");
		scanf("%s", (*pstus)->stu[(*pstus)->sz].number);
		printf("请输入学生家庭住址:");
		scanf("%s", (*pstus)->stu[(*pstus)->sz].add);
		(*pstus)->sz++;
		system("cls");
		printf("添加成功\n");
	}
}

void pritstus(stumgt* pstus)   //打印所有信息
{
	assert(pstus != NULL);
	system("cls");   //输出所有信息前先清屏一次
	printf("%-10s\t%-10s\t%-5s\t%-10s\t%-10s\t%-12s\t%-20s\t\n", "学号", "姓名", "性别", "年龄", "寝室号", "电话号码", "家庭住址");//先设置好对应字段格式
	int i = 0;
	for (i = 0; i < pstus->sz; i++)
	{
		printf("%-10s\t%-10s\t%-5s\t%-10d\t%-10s\t%-12s\t%-20s\t\n",  //按设置好的格式对其打印数据
			pstus->stu[i].sno,
			pstus->stu[i].name,
			pstus->stu[i].sex,
			pstus->stu[i].age,
			pstus->stu[i].roomnum,
			pstus->stu[i].number,
			pstus->stu[i].add);
	}
}
static int findstu(stumgt* pstus, char tmp[])  //封装的内部 查找学生学号信息的函数 找到返回对应的数组元素下标 没找到返回-1
{
	assert(pstus != NULL);
	int i = 0;
	for (i = 0; i < pstus->sz; i++)
	{
		if (strcmp(tmp, pstus->stu[i].sno) == 0)
			return i;
	}
	return -1;
}
void dropstu(stumgt* pstus)  //删除学生信息
{
	assert(pstus != NULL);
	if (pstus->sz == 0)   //判断学生记录是否为空
	{
		printf("当前没有学生信息,无法删除\n");
		return;
	}
	char tmp[20] = { 0 };
	printf("输入要删除的学生的学号信息:");
	scanf("%s", tmp);
	int num = findstu(pstus, tmp);  //使用内部封装的查找函数
	if (num == -1)
	{
		printf("没有找到要删除的学生信息\n");
		return;
	}
	else
	{
		int i = 0;
		for (i = num + 1; i < pstus->sz; i++)
		{
			pstus->stu[i - 1] = pstus->stu[i];
		}
		pstus->sz--;
		printf("已删除学号为%s的学生成员\n", tmp);
	}
}
void search(stumgt* pstus)  //查找学生信息的函数  以名字作为查找标准
{
	assert(pstus != NULL);
	char tmp[20] = { 0 };
	printf("请输入要查找的学生姓名:");
	scanf("%s", tmp);
	system("cls");
	printf("已查询到以下姓名为%s的学生信息↓↓↓\n", tmp);  //找到后按设置的对齐格式输出查找的信息
	printf("%-10s\t%-10s\t%-5s\t%-10s\t%-10s\t%-12s\t%-20s\t\n", "学号", "姓名", "性别", "年龄", "寝室号", "电话号码", "家庭住址");
	int i = 0;
	for (i = 0; i < pstus->sz; i++)
	{
		if (strcmp(tmp, pstus->stu[i].name) == 0)
		{
			printf("%-10s\t%-10s\t%-5s\t%-10d\t%-10s\t%-12s\t%-20s\t\n",
				pstus->stu[i].sno,
				pstus->stu[i].name,
				pstus->stu[i].sex,
				pstus->stu[i].age,
				pstus->stu[i].roomnum,
				pstus->stu[i].number,
				pstus->stu[i].add);
		}
	}
}
static void modifyall(stumgt* pstus)  //内部封装的修改整条学生信息的函数
{
	char tmp[20] = { 0 };
	printf("请输入要修改的学生学号信息:");
	scanf("%s", tmp);

	int num = findstu(pstus, tmp);  //先判断是否存在要修改的学生信息
	if (num == -1)
	{
		printf("未找到要修改的学生信息\n");
		return;
	}
	else
	{
		system("cls"); //输入学生信息前清屏一次
		printf("请输入学生学号:");
		char tmp[20] = { 0 };
		scanf("%s", tmp);
		if (is_repeat(pstus, tmp))   //学生学号为主要属性  应是唯一的且不能为空
		{
			printf("输入失败!!!已存在学号信息为%s的学生,不同学生信息不允许设置相同学号\n", tmp);
			return;
		}

		strcpy(pstus->stu[num].sno, tmp);  //修改后的学号在之前学生信息里没有重复的则将tmp的拷贝修改当前学生信息学号
		printf("请输入学生名字:");
		scanf("%s", pstus->stu[num].name);
		printf("请输入学生性别:");
		scanf("%s", pstus->stu[num].sex);
		printf("请输入学生年龄:");
		scanf("%d", &pstus->stu[num].age);
		printf("请输入学生寝室号:");
		scanf("%s", pstus->stu[num].roomnum);
		printf("请输入学生电话号码:");
		scanf("%s", pstus->stu[num].number);
		printf("请输入学生家庭住址:");
		scanf("%s", pstus->stu[num].add);
		printf("修改成功\n");
	}
}
static void modifysno(stumgt* pstus, int num) //内部封装的单独修改学号的函数
{
	printf("你要将学号修改为:");
	char tmp[20] = { 0 };
	scanf("%s", tmp);
	if (is_repeat(pstus, tmp))
	{
		printf("修改失败,不能输入已有的学号\n");
		return;
	}
	else
	{
		strcpy(pstus->stu[num].sno, tmp);
		printf("修改成功\n");
	}

}
static void modifyname(stumgt* pstus, int num)//内部封装的单独修改名字的函数
{
	printf("你要将名字修改为:");
	scanf("%s", pstus->stu[num].name);
	printf("修改成功\n");
}
static void modifysex(stumgt* pstus, int num)//内部封装的单独修改性别的函数
{
	printf("你要将性别修改为:");
	scanf("%s", pstus->stu[num].sex);
	printf("修改成功\n");
}
static void modifyage(stumgt* pstus, int num)//内部封装的单独修改年龄的函数
{
	printf("你要将年龄修改为:");
	scanf("%d", &pstus->stu[num].age);
	printf("修改成功\n");
}
static void modifyroomnum(stumgt* pstus, int num)//内部封装的单独修改寝室号的函数
{
	printf("你要将寝室号修改为:");
	scanf("%s", pstus->stu[num].roomnum);
	printf("修改成功\n");
}
static void modifynumber(stumgt* pstus, int num)//内部封装的单独修改电话号码的函数
{
	printf("你要将电话号码修改为:");
	scanf("%s", pstus->stu[num].number);
	printf("修改成功\n");
}
static void modifyadd(stumgt* pstus, int num)//内部封装的单独修改地址的函数
{
	printf("你要将地址修改为:");
	scanf("%s", pstus->stu[num].add);
	printf("修改成功\n");
}

static void modifyone(stumgt* pstus)  //内部封装修改整条学生信息里的某项信息函数
{
	char tmp[20] = { 0 };
	printf("请输入要修改的学生学号信息:");
	scanf("%s", tmp);
	int num = findstu(pstus, tmp);  //先判断是否存在该学生信息
	if (num == -1)
	{
		printf("未找到要修改的学生信息\n");
		return;
	}
	else
	{
		int input = 0;
		modifymenu2();  //下面为函数指针数组将一个0和上面7个封装好的修改对应学生信息的函数指针作为数组初始化信息(0是为了方便下标对其选项)
		void(*mod[8])(stumgt*, int) = { 0,modifysno,modifyname,modifysex,modifyage,modifyroomnum,modifynumber,modifyadd };
		printf("请选择修改的内容:");
		do
		{
			scanf("%d", &input);
			if (input >= 8 || input < 0)
			{
				printf("非法输入,请重新选择\n");

			}
			else if (input == 0)
			{
				printf("退回上一层修改菜单\n");
			}
			else
				mod[input](pstus, num); //根据菜单对应input输入的值调用 数组里相对应的函数指针

		} while (input >= 8 || input < 0); //超出重新输入

	}
}
void modifystu(stumgt* pstus)  //修改学生信息的函数
{


	int input = 0;
	do
	{
		modifymenu1();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)//选择修改整条学生信息还是修改一条里某一项的学生信息
		{
		case MODALL:
			modifyall(pstus);
			break;
		case MODONE:
			modifyone(pstus);
			break;
		case END:
			printf("已结束修改操作\n");
			break;
		default:
			printf("非法输入,请重新输入\n");
		}

	} while (input);

}
static void countall(stumgt* pstus)  //内部封装的统计当前所有学生信息记录函数
{
	printf("已统计当前系统中有%d个学生\n", pstus->sz);
	return;
}
static void countage(stumgt* pstus)  //统计年龄范围内学生记录的个数
{
	int age1 = 0;
	int age2 = 0;
	int age = 0;
	printf("请输入两个数字将统计其范围内的年龄个数(如果两个数字相同则统计这个数字年龄的学生个数)");
	scanf("%d%d", &age1, &age2);
	int count = 0;
	int i = 0;
	for (i = 0; i < pstus->sz; i++)
	{
		age = pstus->stu[i].age;
		if (age >= age1 && age <= age2)
		{
			count++;
		}
	}
	printf("在%d岁和%d岁之间的学生个数为%d\n", age1, age2, count);
	return;
}
static void countroom(stumgt* pstus) //统计 寝室号的个数
{
	char room[20] = { 0 };
	printf("请输入寝室号:");
	scanf("%s", room);
	int i = 0;
	int count = 0;
	for (i = 0; i < pstus->sz; i++)
	{
		if (strcmp(room, pstus->stu[i].roomnum) == 0)
		{
			count++;
		}
	}
	printf("寝室号为%s的寝室个数为%d个\n", room, count);
	return;
}
static countsex(stumgt* pstus)   //统计相同性别的学生个数
{
	char sex[5] = { 0 };
	printf("请输入要统计的性别:");
	scanf("%s", sex);
	int i = 0;
	int count = 0;
	for (i = 0; i < pstus->sz; i++)
	{
		if (strcmp(sex, pstus->stu[i].sex) == 0)
		{
			count++;
		}
	}
	printf("性别为%s的学生个数为:%d\n", sex, count);
}
void countstu(stumgt* pstus)  //统计学生信息的函数
{
	assert(pstus != NULL);
	system("cls");

	int input = 0;
	do
	{
		countmenu();
		printf("请选择统计方式:");
		scanf("%d", &input);
		switch (input)  //对应菜单输入input选择 跳转到上面封装的统计函数
		{
		case COUNTALL:
			countall(pstus);
			break;
		case COUNTAGE:
			countage(pstus);
			break;
		case COUNTROOM:
			countroom(pstus);
			break;
		case COUNTSEX:
			countsex(pstus);
			break;
		case ENDCOUNT:
			printf("已结束统计\n");
			break;
		default:
			printf("非法输入,请重新选择\n");
		}

	} while (input);
}
int cmp_sno(const studata stu1, const studata stu2) // 为qsort 函数设置的函数指针 用于比较两个元素的大小传给qsort
{
	return strcmp(stu1.sno, stu2.sno);    //比较两个学生里的学号大小 进行排序 这两个学生元素
}
void sortstu(stumgt* pstus) //  升序排序所有学生记录的函数
{
	if (pstus->sz == 0)   //判断排序时是否为空
	{
		printf("当前学生记录为空,无法排序\n");
		return;
	}
	qsort(pstus->stu, pstus->sz, sizeof(studata), cmp_sno);    // 排序
	printf("已按学号完成升序排序\n");
}

五.总结

本篇博客实现了将学生信息管理系统静态版变为动态内存版本.主要涉及了枚举类型,动态内存增长函数和柔性数组的使用,动手敲代码才能使所学的知识更为牢固
博主在后续还会更新学生信息管理系统(3.0文件存储版本)

有关赶紧进来看看---C语言实现学生信息管理系统(2.0动态内存版)的更多相关文章

  1. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  2. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  3. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  4. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  5. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  6. ruby-on-rails - Ruby 中的内存模型 - 2

    ruby如何管理内存。例如:如果我们在执行过程中采用C程序,则以下是内存模型。类似于这个ruby如何处理内存。C:__________________|||stack|||------------------||||------------------|||||Heap|||||__________________|||data|__________________|text|__________________Ruby:? 最佳答案 Ruby中没有“内存”这样的东西。Class#allocate分配一个对象并返回该对象。这就是程序

  7. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  8. ruby-on-rails - 如何在 Ruby on Rails 中实现由 JSF 2.0 (Primefaces) 驱动的 UI 魔法 - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道ruby​​onrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim

  9. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  10. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

随机推荐