结构体
C语言的结构体(struct)从本质上讲自定的数据类型,只不过这种数据类型比较复杂,是由int、float、char等基本数据类型组成。是一种聚合数据类型。
在实际开发中,我们可以将一组类型不同,但是用来描述同一件事物的变量放到结构体中。比如:学生有名字、年龄、身高、成绩等多个属性。使用结构体之后,就不需要零散定义多个变量,可以将他们都放到结构体中。
1.1 结构体概述
数组是用来存储一组相同数据类型的集合,如果要存储不同数据类型,那么数组就满足不了需求,这个时候用到结构体。
C语言结构体的格式:
struct 结构体名{
结构体所要包含的数据类型
};
我的理解是:结构体是一种集合,里面定义了多个变量或者数组。他们的数据类型可以相同,可以不同,每一个这样的变量或者数组,称为结构体的成员。一般的数据类型只能定义一个变量,数组类型可以定义多个相同类型变量,而结构体可以定义多个不同类型变量。
struct Student
{
char name[30];//学生姓名
int age;//学生年龄
float tall;//学生身高
double score;//学生成绩
};
Student结构体名,包含4个成员:name、age、tall、score。结构体成员的定义和普通数据类型的定义相同,只是不做初始化。
1.2结构体变量
既然结构体是一种自定义的数据类型,那么就可以通过它来定义变量,比如
struct Student stu1,stu2,stu3;
使用结构体Student创建了三个结构体变量,分别是stu1、stu2、stu3。
注意:定义结构体变量时,struct关键字不能少。(struct Student是一个数据类型,单struct或Student定义变量是不行的)
可以将结构体Student看成一个模板,定义出来的变量都具有相同的性质。也可以在定义结构体的时候,同时定义结构体变量,只需要将变量名放到结构体定义后面即可。
在这里,stu2是在定义结构体时,同时定义变量;stu1是使用struct Student这个数据类型定义的
#include <stdio.h>
struct Student
{
char name[30];//学生姓名
int age;//学生年龄
float tall;//学生身高
double score;//学生成绩
}stu2;
int main()
{
struct Student stu1;
return 0;
}
也可以在定义结构体的时候不给结构体名字,这种没有名字的结构体,称为:匿名结构体。
struct
{
int a;
float b;
double c;
char d;
}node1,node2;
因为匿名结构体没有名字,所以后续没有办法再给这个结构体定义结构体变量,只能在定义结构体的时候创建变量。匿名结构体只能在定义结构体时创建变量
1.3结构体成员的赋值和获取
结构体中使用点号(.)来访问结构体成员,格式
结构体变量名.成员名;
#include <stdio.h>
#include <string.h>
struct Student
{
char name[30];//学生姓名
int age;//学生年龄
float tall;//学生身高
double score;//学生成绩
}stu2;//结构体变量stu2
int main()
{
struct Student stu1;//结构体变量stu1
stu1.age=18;
stu1.tall=180;
stu1.score=99.5;
//stu1.name="小明";//数组不能这样赋值
strcpy(stu1.name,"小明");
printf("%s %d %.1f %.1lf\n",stu1.name,stu1.age,stu1.tall,stu1.score );
return 0;
}
除了这种对成员变量单一赋值的方法,也可以在定义时整体赋值(注意这里,只能在定义的时候整体赋值,否则只能单个赋值)
这里主要是因为当结构体变量被声明后,它在内存中已经有了确定的存储位置和布局。如果允许整体赋值,可能会引发内存管理上的混乱。
#include <stdio.h>
#include <string.h>
struct Student
{
char name[30];//学生姓名
int age;//学生年龄
float tall;//学生身高
double score;//学生成绩
}stu2={"张三",29,175,100};//结构体变量stu2
int main()
{
struct Student stu1;//结构体变量stu1
stu1.age=18;
stu1.tall=180;
stu1.score=99.5;
//stu1.name="小明";//数组不能这样赋值
strcpy(stu1.name,"小明");
printf("%s %d %.1f %.1lf\n",stu1.name,stu1.age,stu1.tall,stu1.score );
printf("%s %d %.1f %.1lf\n",stu2.name,stu2.age,stu2.tall,stu2.score );
//只能在定义时整体赋值
// struct Student stu3;
// stu3={"tom",30,160,80};
// printf("%s %d %.1f %.1lf\n",stu3.name,stu3.age,stu3.tall,stu3.score );
return 0;
}
这种方式仅限于定义结构体变量的时候,在使用过程中,只能对成员逐一赋值。
需要注意的是,结构体是一种自定义的数据类型,是创建变量的模板,他不占用内存,结构体变量包含实实在在的数据,需要内存来存储。
1.4结构体成员修改
结构体成员只能单个修改不能整体赋值。
给字符型数组和字符型指针赋值,只能用strcpy()
#include <stdio.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct A
{
char node1[30];
char *node2;
int node3;
};
int main()
{
//通过指针的形式先开辟空间使用等于号赋值,用数组的形式需要用函数strcpy()
struct A a;
strcpy(a.node1,"lllll");
//开辟空间
a.node2=(char *)malloc(30);
a.node2="aaaaa";
printf("%s\n",a.node1 );
printf("%s\n",a.node2 );
a.node3=100;
printf("%s %s %d\n",a.node1,a.node2,a.node3 );
printf("=====================================\n");
a.node3=50;
strcpy(a.node1,"LLLLL");
a.node2="AAAAA";
printf("%s %s %d\n",a.node1,a.node2,a.node3 );
a.node1[0]='Z';
printf("%s %s %d\n",a.node1,a.node2,a.node3 );
return 0;
}
1.5结构体数据大小
结构体大小不是结构体中所有变量大小的和。
1.补齐
struct A
{
int a;//4
char b;//1
};//8
2.寻址
struct B
{
int data;//4
char a;//1
float b;//4
int *c;//8
int e;//4
};//32
1.6清空结构体
清空结构体中的数据:memset
memset(&s,int ch,size_t n);
表示:将s中当前位置后面n个字节用ch来替换。
memset:作用是在一段内存中填充某个给定的值。它是对较大结构体或者数组清0的一种方式。
memset(&stu1,0,sizeof(struct Student));
#include <stdio.h>
#include <string.h>
struct Student
{
char *name;
int age;
float score;
char job[20];
}stu1={"张三",18,99.5,"computer"};
int main()
{
printf("%s %d %f %s\n",stu1.name,stu1.age,stu1.score,stu1.job );
memset(&stu1,0,sizeof(struct Student));
printf("%s %d %f %s\n",stu1.name,stu1.age,stu1.score,stu1.job );
return 0;
}
1.7结构体数组
所谓的结构体数组,是指数组中每个元素都是结构体变量,在实际开发中C语言结构体数组常被用来表示一个拥有相同数据结构的群体。
#include <stdio.h>
#include <stdlib.h>
#define LEN 2
struct Student
{
char *name;
int num;//学号
char sex;//M W
float score[3];
char job[20];//专业
}clsz[LEN];
int main()
{
//struct Student stu1;
for (int i = 0; i < LEN; ++i)
{
printf("请输入%d位学生信息\n",i+1 );
printf("请输入姓名:");
char *addname=(char *)malloc(20);
scanf("%s",addname);
clsz[i].name=addname;
printf("请输入人学号:");
scanf("%d",&clsz[i].num);
getchar();//获取回车这个字符
printf("请输入性别:");
scanf("%c",&clsz[i].sex);
printf("请输入语文成绩:");
scanf("%f",&clsz[i].score[0]);
printf("请输入数学成绩:");
scanf("%f",&clsz[i].score[1]);
printf("请输入英语成绩:");
scanf("%f",&clsz[i].score[2]);
printf("请输入专业:");
scanf("%s",clsz[i].job);
}
for (int i = 0; i < LEN; ++i)
{
printf("%s %d %c %.1f %.1f %.1f %s\n",clsz[i].name,clsz[i].num,clsz[i].sex,clsz[i].score[0],clsz[i].score[1],clsz[i].score[2],clsz[i].job );
}
return 0;
}
1.8结构体嵌套
结构体内部的结构体,必须要定义结构体变量才能使用。
#include <stdio.h>
// struct score
// {
// float math;
// float computer;
// float chinese;
// };
struct Student
{
char *name;
int num;
struct score
{
float math;
float computer;
float chinese;
}sc;
//struct score sc;
};
int main()
{
struct Student stu1;
stu1.name="qlf";
stu1.num=1001;
stu1.sc.math=99.5;
stu1.sc.computer=80.5;
stu1.sc.chinese=100;
printf("%s %d %f %f %f\n",stu1.name,stu1.num,stu1.sc.math,stu1.sc.computer,stu1.sc.chinese );
struct Student stu2={"lll",1002,80,50,100};
printf("%s %d %f %f %f\n",stu2.name,stu2.num,stu2.sc.math,stu2.sc.computer,stu2.sc.chinese );
return 0;
}
1.9结构体指针
1.9.1结构体指针定义
当一个指针变量指向结构体时,我们就称它为结构体指针,C语言结构体定义的形式:
struct 结构体名 * 变量名;
#include <stdio.h>
struct Student
{
char *name;
int num;//学号
char sex;//M W
double score;
}stu1={"qlf",1001,'M',99.5},* stp2=&stu1;
int main()
{
//定义了一个结构体指针stp,指向stu1
struct Student * stp=&stu1;
return 0;
}
需要注意的是:
结构体变量名和数组变量名不同,数组名在表达式中会被转换成数组的指针,而结构体名不会,无论在任何的表达式中他表示的是整个集合的本身。要想取得结构体变量的地址,必须在前面加上&。结构体变量名并不是结构体首地址,也不能像使用数组名一样使用结构体变量名。
struct Student *pstu=&Student;
struct Student *pstu=Student;
不能取一个结构体名的地址,也不能将它赋值给其他变量。
1.9.2结构体指针访问结构体成员
格式:struct Student stu1,*pointer=stu1;
stu1.member;
(*pointer).member;
pointer->member;
需要注意的问题:
1.第一种写法的.点优先级高于*,()不能省略掉,如果省略*pointer.member,这样意义不对
2.第二种写法:->是一个运算符,通过结构体指针直接获取结构体成员
#include <stdio.h>
struct Student
{
char *name;
int num;
int age;
char group;
double score;
}stu1={"Tom",1001,18,'A',99.5},*pstu1=&stu1;
int main()
{
struct Student *pstu2=&stu1;
printf("%s %d %d %c %lf\n",stu1.name,stu1.num,stu1.age,stu1.group,stu1.score );
printf("%s %d %d %c %lf\n",(*pstu1).name,(*pstu1).num,(*pstu1).age,(*pstu1).group,(*pstu1).score);
printf("%s %d %d %c %lf\n",pstu2->name,pstu2->num,pstu2->age,pstu2->group,pstu2->score);
return 0;
}
1.9.3结构体数组指针
#include <stdio.h>
struct Student
{
char *name;
int num;
int age;
char group;
double score;
}clasz[3]={
{"tom",1001,18,'A',99.5},
{"jerry",1002,20,'B',100},
{"marry",1003,30,'C',60}
},*ps;
int main()
{
//数组长度
int len=sizeof(clasz)/sizeof(clasz[0]);
printf("%d\n",len );
for(int i=0;i<len;i++)
{
printf("%s %d %d %c %lf\n",i[clasz].name,i[clasz].num,i[clasz].age,i[clasz].group,i[clasz].score );//a[i]-->*(a+i)-->*(i+a)-->i[a]
//printf("%s %d %d %c %lf\n",(*(clasz+i)).name,(*(clasz+i)).num,(*(clasz+i)).age,(*(clasz+i)).group,(*(clasz+i)).score );
}
// for (ps=clasz;ps<clasz+len;ps++)
// {
// //printf("%s %d %d %c %lf\n",ps->name,ps->num,ps->age,ps->group,ps->score);
// //printf("%s %d %d %c %lf\n",(*ps).name,(*ps).num,(*ps).age,(*ps).group,(*ps).score);
// }
return 0;
}
1.9.4结构体指针作为函数参数
结构体变量名代表整个集合数据的本身,作为函数参数时传递的是整个集合数据,也就是所有的成员。而不像数组会被编译转换为一个指针。如果结构体成员较多,传递的时间和空间开销就会很大。所以最好的办法就是用结构体指针,这是由实参向形参传递一个地址,速度非常快。
总而言之,指针除了对初学者不友好以外,它真是个好东西
#include <stdio.h>
struct Student
{
char name[10];
int age;
float score;
};
//函数结束之后,形参stu空间会被释放
void inputStudent(struct Student stu)
{
printf("请输入姓名:");
scanf("%s",stu.name);
printf("请输入年龄:");
scanf("%d",&stu.age);
printf("请输入分数:");
scanf("%f",&stu.score);
}
void inputStudent2(struct Student *stu)
{
printf("请输入姓名:");
scanf("%s",stu->name);
printf("请输入年龄:");
scanf("%d",&stu->age);
printf("请输入分数:");
scanf("%f",&stu->score);
}
void showStudent(struct Student stu)
{
printf("%s %d %f\n",stu.name,stu.age,stu.score );
}
void showStudent2(struct Student * stu)
{
printf("%s %d %f\n",stu->name,stu->age,stu->score );
}
int main()
{
struct Student stu1;
//inputStudent(stu1);
inputStudent2(&stu1);
//showStudent(stu1);
showStudent2(&stu1);
return 0;
}
练习:定义一个学生结构体,结构体成员包括姓名、年龄、分数,通过函数的方式执行输入和遍历,计算平均分和60分以下学生人数。
#include <stdio.h>
struct Student
{
char name[20];
int age;
float score;
}clasz[3],*ps=clasz;
void inputStudent(struct Student *stu,int len)
{
for (int i = 0; i < len; ++i)
{
printf("请输入姓名:");
scanf("%s",(stu+i)->name);
printf("请输入年龄:");
scanf("%d",&(stu+i)->age);
printf("请输入分数:");
scanf("%f",&(stu+i)->score);
}
}
void outputStudent(struct Student *stu,int len)
{
for (int i = 0; i < len; ++i)
{
printf("%s %d %f\n",(stu+i)->name,(stu+i)->age,(stu+i)->score );
}
}
float avg(struct Student *stu,int len)
{
float avg=0;
for (int i = 0; i < len; ++i)
{
avg+=(stu+i)->score;
}
avg=avg/len;
return avg;
}
int num(struct Student *stu,int len)
{
int sum=0;
for (int i = 0; i < len; ++i)
{
if((stu+i)->score<60)
{
sum++;
}
}
return sum;
}
int main()
{
int len=sizeof(clasz)/sizeof(clasz[0]);
inputStudent(ps,len);
outputStudent(ps,len);
printf("平均分:%f\n",avg(ps,len) );
printf("不及格人数:%d\n",num(ps,len) );
return 0;
}
1.9.5结构体成员添加函数指针
结构体成员是函数指针
结构体中除了可以定义变量外,也可以添加函数行为。
#include <stdio.h>
#include <string.h>
struct Student{
char name[10];
int age;
void (*pfunc)(void);//函数指针,指向一个无返回无参数的函数的指针
//void *pfunc(void);//返回值为指针的函数,指针函数
void (*padd)(int a,int b);
};
void study()
{
printf("学习\n");
}
void play()
{
printf("玩\n");
}
void add(int a,int b)
{
printf("%d\n",a+b );
}
int main()
{
struct Student stu1,stu2;
strcpy(stu1.name,"qlf");
stu1.age=18;
//函数指针指向对应的函数
stu1.pfunc=study;
//调用结构体中指针函数
stu1.pfunc();
stu1.pfunc=play;
stu1.pfunc();
strcpy(stu2.name,"王钢蛋");
stu2.age=30;
stu2.padd=add;
//调用函数指针
stu2.padd(1,2);
return 0;
}
练习:定义一个矩形结构体,在结构体中声明一个计算矩形面接的函数指针,在main中调用结构体的函数,实现计算矩形面积。
#include <stdio.h>
struct Rect
{
double width;//长
double height;//宽
double (*parea)(double width,double height);
};
//计算面积的函数
double area(double width,double height)
{
return width*height;
}
int main()
{
struct Rect rect;//通过结构体定义结构体变量
rect.width=3.14;
rect.height=4;
rect.parea=area;
double result=rect.parea(rect.width,rect.height);//定义变量保存返回值
printf("矩形面积为:%lf\n",result );
return 0;
}
1.9.6 typedef在结构体中的应用
这里就是使用typedef为Student结构体取了一个别名,之后初始化一个变量就不需要用struct Student,直接使用别名 变量名;就可以了。
#include <stdio.h>
typedef struct Student
{
int age;
float score;
}Stu,*STU;
int main()
{
Stu stu1={17,99.5};
printf("%d %f\n",stu1.age,stu1.score );
//原类型依然可以用
struct Student stu2={18,99};
printf("%d %f\n",stu2.age,stu2.score );
// Stu Stu={17,99.5};
// printf("%d %f\n",Stu.age,Stu.score );
STU pstu=&stu1;
printf("%d %f\n",pstu->age,pstu->score );
return 0;
}
这里有一个点需要注意一下
为匿名结构体取别名应该怎么取呢,
typedef struct
{
int age;
float score;
}Stu;
这样就可以了。
要熟记typedef取别名的规则:
1.定义一个要取别名的数据类型的变量,例如int a;
2.前面加上typedef,例如typedef int a;
3.将变量名修改成你想取的别名,例如typedef int INT;这样就为int取了一个别名INT,以后可以使用int定义变量,同样可以使用INT定义变量:int b;等同于INT b;