C语言---结构体与malloc浅析
前言:解决学生管理系统类问题的理论基础和实例=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=