一、什么是结构体?
结构体是一种用户自己定义的一种数据类型,可以用它来定义一些复杂的变量,例如一个学生的信息会包含学号,姓名,性别,成绩等。在C语言中想要用一个变量来描述它,就需要引入我们本文中介绍的东西——结构体。
二、结构体类型的声明
语法
struct 结构体名称
{
类型名1 成员名1;
类型名2 成员名2;
~~~~~~ ~~~~~~;
};
示例
struct student
{
int id;
char name[20];
float s;
};//千万不要忘记有一个‘;’
三、结构体变量的定义
先声明你想要的结构体 student 这个变量,然后就可以把它当int之类的数据类型一样使用了(但是都要在前面加一个 struct。这看起来不大方便,因此我们就会引入 typedef 来为它重命名,详见下一段)。
struct student //声明结构体变量
{
int id;
char name[20];
float score;
};
struct student stu1,stu2; //定义结构体变量
在定义结构体时,C语言可以借助 typedef 来给已存在的结构体起一个别名,这样就可以直接引用了。
typedef struct student //声明结构体变量
{
int id;
char name[20];
float score;
}STU;
STU stu1,stu2;
PS:编译器不会像 int 一样给 struct student 分配空间,只会给结构体变量stu1,stu2分配空间。
四、结构体的实际储存
#include<stdio.h>
struct text
{
char a;
double b;
int c;
};
struct text t;
int main()
{
printf("%d-%d-%d",sizeof(t.a),sizeof(t.b),sizeof(t.c));
printf("%d",sizeof(t));
}
这个程序运行后会发现sizeof(t. a)=1,sizeof(t. b)=8, sizeof(t.c)=4,三者之和为13,但是sizeof(t)=24,结构体变量实际所占空间大于其各成员所占空间之和。从计算机组成和工作原理出发,这里涉及一个存储时的字节对齐问题,编译器默认会对结构体做以下处理。
①结构体变量中每个成员存放到内存中时,成员的起始地址与结构体首地址的偏移量,必为该成员宽度的整数倍(以结构体变量首地址为0计算)。如一个宽度为4的成员(int)位于从结构体首地址开始的能被4整除的偏移地址上,一一个宽度为8的成员(double)位于能被8整除的偏移地址上(可以理解为找成员的最大公倍数)。
②结构体每个成员相对于结构体首地址的偏移量都是成员数据类型大小的整数倍,在此规则下,若相邻成员不能存放在相邻空间,编译器会在成员之间加上填充字节。
③结构体变量总的存储单元为其最宽的基本类型成员大小的整数倍,如果按照前面规则计算出来的存储单元不是最宽的那个成员大小的整数倍,则在最末尾的一个成员之后加上填充字节。
根据这三条规则来分析上面的这个例子。按照结构体内各成员的顺序,先存储char型成员a,假设存在编号0的字节;其次存放double型成员b,需要8B,顺序检查存储单元后发现,前面一个8B空间已被a占用,因此将b存入第二个8B空间,即存入第8~15B;接着存放int型成员c,需要4B,前面的四个4B空间(0~15)都已被占用,因此将c存入第五个4B空间,即第16~ 19B处。此时占用空间为20B,根据规则三,本例总的存储单元应为8(double型数据宽度)的整数倍,即24,因此第20~23个字节用来补齐到8的整数倍,可见结构体变量t实际占用的存储空间是24。
int a | double b | int c | 补齐到8的整数倍 |
0 1 2 3 4 5 6 7 8 9 10 12 13 14 15 16 17 18 19 20 21 22 23
六、结构体的使用和初始化
值得注意的是使用结构体成员时要用“.”来引用
#include<stdio.h>
typedef struct student //声明结构体变量
{
int id;
char name[20];
float score;
}STU;
STU stu1,stu2;
int main()
{
scanf("%d %c %f",&stu1.id,stu1.name,&stu1.score);//逐个输入结构体各成员
printf("%d-%c-%f",stu1.id,stu1.name[20],stu1.score);//逐个输出结构体各成员
}
七、结构体数组
结构体数组与数值型数组的构成是相似的,不同之处在于结构体数组的每个数组元素都是结构体变量
typedef struct student //声明结构体变量
{
int id;
char name[20];
float score;
}STU;
STU stu1[3]={{1234,"ann",92},{1235,"canney",90},{1236,"bob",99}}; //定义并初始化结构体数组
同样的,引用结构体数组成员时要用“.”来引用。例:stu1[1].id。
八、结构体指针
结构体指针必须“先赋值,后使用”。赋值是把结构体的首地址赋给指针变量,而不是把结构名赋给指针变量。详见下图
struct student stu1;//定义结构体变量
struct student *p; //定义结构体指针
p=&stu1 //把首地址赋给p指针
注意指针不能指向结构体成员,但是能通过指针来访问结构体成员
typedef struct student //声明结构体变量
{
int id;
char name[20];
float score;
}STU;
int main()
{
STU stu1
STU *p;
p=&stu1;
scanf("%d %c %f",&stu1.id,stu1.name,&stu1.score);//逐个输入结构体各成员
printf("%d-%c-%f",p->id,p->name,p->score);//逐个输出结构体各成员
}
结构体数组指针的定义方式其实就是将结构体数组与结构体指针这两部分知识的融合
struct student stu;
struct student *p;
p=stu; //注意哦这里有变化!
p是指向结构体数组的stu指针变量,保存了数组的首地址,即p指向该结构体的0号结构体元素,p+1指向一号结构体元素······
此外对结构体元素成员的引用形式如下
(p+i)->id (p+i)->name (p+i)->score