C语言---结构体与malloc浅析(学生管理系统类问题)

发布于:2022-12-16 ⋅ 阅读:(404) ⋅ 点赞:(0)


前言:解决学生管理系统类问题的理论基础和实例=v=

一、结构体定义

C语言中存在数据类型的概念,例如int、float、char;它们分别代表整型、浮点型和字符型数据,而当某个信息无法单纯用单个类型的数据表示时,就需要使用结构体;如描述一个人的性别、年龄,某个物体的编号和形状等。结构体就是由多种不同类型的数据组合而成的新数据类型。

例如,可以定义一个student结构体,用来记录学生的学号、姓名、年龄、成绩、家庭住址等信息,这个student数据类型也可以和基本数据类型一样,来定义其他变量的数据类型。

结构体的一般形式为:

struct 结构体名
{
	数据类型  成员名1;
	数据类型  成员名2;
	:
	数据类型  成员名n;
}

定义中的数据类型本身也可以是一个结构体类型,以;作为结束符,整个结构的定义也以分号作为结束符。

例如,定义一个如下的student结构体:

struct student
{
	double ID;
	char name[30];
	int age;
	float grade;
	char address[50];
};
int age = 18

需要注意的是,结构体中的成员名可以与程序中的变量名相同,但二者是完全不同的对象。

二、结构体变量的声明、使用和初始化

1、结构体变量的声明

在定义了一个结构体之后,就可以用它来声明一个结构体类型的变量,他有以下几种形式:
形式1:

struct student
{
	double ID;
	char name[30];
	int age;
	float grade;
	char address[50];
};
	struct student a;

这里的student是结构体的标识符,不是变量名,声明的结构体变量名为a。

形式2:

struct student
{
	double ID;
	char name[30];
	int age;
	float grade;
	char address[50];
}a;

该方法为在结构体定义的末尾直接定义结构体变量a。

形式3:

typedef struct student
{
	double ID;
	char name[30];
	int age;
	float grade;
	char address[50];
}STU;
	STU a;

使用typedef定义结构体时,末尾的STU不再代表定义的变量名,而是代表struct student这个数据类型。

※一个结构体变量占用内存的实际大小可以使用sizeof求出,它的内存分配原理是按照所占大小最高的成员类型 字节数的整数倍进行分配。

2、结构体变量的使用和初始化

结构体变量是一个不同数据类型的若干数据的集合体,在程序中使用时,不能把它作为一个整体参加数据处理,实际参加数据操作的是结构体变量的各个成员数据。

以上文中的定义的结构体变量a为例,结构体变量的成员一般用如下方法表示:

结构体变量名.成员名
a.ID;
a.name;
a.age;
a.grade;
a.address;

此时,可以使用不同的赋值方法对每个成员进行赋值:

a.ID = 0001;
strcpy(a.name,"Kikyo");
a.age = 18;
a.grade = 95.3;
strcpy(a.address,"Earth City");

也可以使用如下方式进行初始化:

struct student a = {0001,"Kikyo",18,95.3,"Earth City"};

需要注意,不能将一个结构体类型变量作为一个整体进行引用,只能对其中的各个成员分别引用;而如果成员本身是一个结构体类型,则需要用若干个成员运算符,直到找出最低一级成员,才能对其进行赋值或运算。

三、结构体数组

1、结构体数组的定义

结构体数组即是由多个结构体变量组成的数组,数组中的每一个元素都是结构体类型的数据;在定义时,只需额外说明其为数组即可,同样有以下几种定义方式:

typedef struct student
{
	double ID;
	char name[30];
	int age;
	float grade;
	char address[50];
}STU;
	STU a[10]
struct student
{
	double ID;
	char name[30];
	int age;
	float grade;
	char address[50];
}a[10];

这两种方式都定义了一个结构体数组a,其中含有十个元素,元素类型都是结构体。

2、结构体数组的初始化

结构体数组在定义时也可以进行初始化,并且与结构体变量的初始化规定相同,
我们举个例子:

typedef struct student
{
	double ID;
	char name[30];
	int age;
	float grade;
	char address[50];
}STU;
	STU a[2] = {{0001,“Kikyo1”,18,95.2,"Earth City"},{0002,“Kikyo2”,17,90.2,"Earth City"}};

如上,对一个含有两个结构体元素的结构体数组a进行了初始化。

3、结构体数组的使用

一个结构体数组的元素相当于一个结构体变量,因此前文关于结构体变量的使用规则同样适用于结构体数组,以上文定义的结构体数组a[2]为例:
引用某一元素中的成员:

printf("学生的ID为%d\n"a[1].ID);
printf("学生的ID为%d\n"(a+1)->ID);

此种方法即输出了第二个元素中学生的ID,即0002。
输入同理:

scanf("%s%d",a[0].name,&a[0].age);
scanf("%s%d",a->name,&a->age);

即输入了第一个元素中学生的姓名和年龄。
可以看到,有两种不同的引用方式:第一种,使用下标法引用,a[0]即结构体数组的第一个元素,将其看作一个整体套用结构体引用的规则,即a[0].name为第一个元素中的成员–name。
第二种是使用数组名进行指引用,“->”的作用可以间接理解为“解引用到成员”,结构体数组名即结构体数组的首地址,对其解引用得到第一个元素的首地址,再定位具体的成员。

关于数组名和指针,如有不明白的可以参考我之前的文章:https://blog.csdn.net/Solahalo/article/details/126630591?spm=1001.2014.3001.5502

四、结构体指针

顾名思义,结构体指针,即是一个指向结构体变量的指针,该指针变量的值是结构体变量的起始地址。
如:

STU *p;

这就是一个struct student类型的结构体指针p。

使用它的方法同样是指引用,以上文的程序为例:

#include<stdio.h>

int main(int argc, const char *argv[]{
	STU a[2] = {{0001,“Kikyo1”,18,95.2,"Earth City"},{0002,“Kikyo2”,17,90.2,"Earth City"}};
	STU *p = a;
	printf("%d\n",p->age);
	printf("%d\n",a->age);
	return 0;
}

输出的结果为 18 18
可以看到,p->age与a->age输出的结果都一致,但p是一个结构体指针变量,而a是一个结构体数组名,它是一个地址常量。

五、结构体与malloc(附实例)

要解决本文的核心目标–学生管理系统问题,首先要学会使用结构体指针与malloc结合开辟空间;
使用malloc开辟空间时,如果使用调用子函数的方式,需要先创建一个结构体指针,将结构体指针取地址传给形参,在子函数中,将malloc申请的地址赋值给解引用后的形参,即可在子函数中使用malloc申请地址。

具体示例如下:以下程序为一个标准的学生管理系统,它的功能有菜单、输入、输出、计算某门成绩的平均分、找出两门以上不及格的学生、找出均分90以上或全科85以上的学生。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define SIZE 5

enum Bugvalue
{
	NULL_ERROR = -3,
	MALLOC_ERROR,
	ERROR,
	TRUE,
	MALLOC,
	INPUT,
	OUTPUT,
	AVERAGE,
	FAIL,
	EX,
	QUIT,
};

typedef struct student
{
	char Name[16];
	int ID;
	float Math;
	float Physics;
	float Chemistry;
}STU;

void menu()
{
	printf("*********Student contral system*************\n");
	printf("1、------------------------malloc\n");
	printf("2、------------------------录入数据\n");
	printf("3、------------------------输出数据\n");
	printf("4、------------------------计算数学平均分\n");
	printf("5、------------------------找出两门以上不及格的学生\n");
	printf("6、------------------------找出均分90以上或全科85以上的学生\n");
	printf("7、------------------------退出\n");
	printf("********************************************\n");

}

int _malloc(STU **p)
{
	if(NULL == p)
	{
		printf("NULL ERROR\n");
		return NULL_ERROR;
	}
	*p = (STU *)malloc(sizeof(STU) * SIZE);
	if(NULL == *p)
	{
		printf("MALLOC ERROR\n");
		return MALLOC_ERROR;
	}
	memset(*p,'\0',sizeof(STU) * SIZE);
	return TRUE;
}

int input(STU *p,int size)
{
	int i;
	if(NULL == p)
	{
		printf("NULL ERROR\n");
		return NULL_ERROR;
	}
	for(i = 0;i < size;i++)
	{
		printf("输入第%d个学生的姓名、学号和三门成绩\n",i+1);
		scanf("%s%d%f%f%f",(p+i)->Name,&(p+i)->ID,&(p+i)->Math,&(p+i)->Physics,&(p+i)->Chemistry);
	}
	return TRUE;
}

int output(STU *p,int size)
{
	int i;
	if(NULL == p)
	{
		printf("NULL ERROR\n");
		return NULL_ERROR;
	}
	for(i = 0;i < size;i++)
	{
		printf("第%d个学生的信息为:\n",i+1);
		printf("%s  %d  %.1f  %.1f  %.1f\n",(p+i)->Name,(p+i)->ID,(p+i)->Math,(p+i)->Physics,(p+i)->Chemistry);
	}
}

int _average(STU *p,float *average)
{
	int i = 0;
	float sum = 0;
	if(NULL == p)
	{
		printf("NULL ERROR\n");
		return NULL_ERROR;
	}
	for(i = 0;i < SIZE;i++)
	{
		sum += (p+i)->Math;
	}
	*average = sum/SIZE;
	return TRUE;
}

int _fail(STU *p,int *newsize,STU *pnew)
{
	int i,j = 0;
	if(NULL == p)
	{
		printf("NULL ERROR\n");
		return NULL_ERROR;
	}
	for(i = 0;i < SIZE;i++)
	{
		if((p+i)->Math < 60 || (p+i)->Physics < 60)
		{
			if((p+i)->Chemistry < 60)
				*(pnew+j) = *(p+i);
			else if((p+i)->Math < 60 && (p+i)->Physics < 60)
				*(pnew+j) = *(p+i);
			j++;
		}
	}
	*newsize = j;
	return TRUE;
}

int _ex(STU *p,int *newsize,STU *pnew)
{
	int i,j = 0;
	float sum = 0;
	if(NULL == p)
	{
		printf("NULL ERROR\n");
		return NULL_ERROR;
	}
	for(i = 0;i < SIZE;i++)
	{
		sum = 0;
		sum = ((p+i)->Math + (p+i)->Physics + (p+i)->Chemistry)/3;
		if(sum >= 90 || ((p+i)->Math >= 85 &&(p+i)->Physics >= 85 &&(p+i)->Chemistry >= 85))
		{
			*(pnew+j) = *(p+i);
			j++;
		}
	}
	*newsize = j;
	return TRUE;
}

int main(int argc, const char *argv[])
{
	STU *p = NULL;
	STU *pnew = NULL;
	int ret,me,size = 0;// ret-> return  me -> menu  size --new size
	float average = 0;
	while(1)
	{
		menu();
		printf("请选择以上功能的一种:\n");
		scanf("%d",&me);
		switch(me)
		{
		case MALLOC:
			{
				ret = _malloc(&p);
				if(ret < 0)
				{
					printf("ERROR\n");
					return ERROR;
				}
				ret = _malloc(&pnew);
				if(ret < 0)
				{
					printf("ERROR\n");
					return ERROR;
				}
				printf("申请初始空间成功\n");
				break;
			}
		case INPUT:
			{
				ret = input(p,SIZE);
				if(ret < 0)
				{
					printf("ERROR\n");
					return ERROR;
				}
				printf("录入信息成功!\n");
				break;
			}
		case OUTPUT:
			{
				ret = output(p,SIZE);
				if(ret < 0)
				{
					printf("ERROR\n");
					return ERROR;
				}
				printf("所有学生信息输出完毕\n");
				break;
			}
		case AVERAGE:
			{
				ret = _average(p,&average);
				if(ret < 0)
				{
					printf("ERROR\n");
					return ERROR;
				}
				printf("数学成绩的平均分为%.1f\n",average);
				break;
			}
		case FAIL:
			{
				ret = _fail(p,&size,pnew);
				if(ret < 0)
				{
					printf("ERROR\n");
					return ERROR;
				}
				printf("筛选完成\n");
				ret = output(pnew,size);
				if(ret < 0)
				{
					printf("ERROR\n");
					return ERROR;
				}
				printf("输出完成\n");
				break;
			}
		case EX:
			{
				ret = _ex(p,&size,pnew);
				if(ret < 0)
				{
					printf("ERROR\n");
					return ERROR;
				}
				printf("筛选完成\n");
				ret = output(pnew,size);
				if(ret < 0)
				{
					printf("ERROR\n");
					return ERROR;
				}
				printf("输出完成\n");
				break;
			}
		case QUIT:
			{
				return TRUE;
			}
		}
	}
	free(p);
	p = NULL;
	free(pnew);
	pnew = NULL;
	return TRUE;
}

码字不易,如果觉得有用,请点个赞哈。
=v=


网站公告

今日签到

点亮在社区的每一天
去签到