前言:
我们都已经学了很多int char …等类型还学到了同类型元素构成的数组,以及取上述类型的指针,在一些小应用可以灵活使用,然而,在实际问题中有时候我们需要几种数据类型一起来修饰某个变量。
例如一个学生的信息就需要学号(字符串),姓名(字符串),年龄(整型)等等。
这些数据类型都不同但是他们又是表示一个整体,要存在联系,那么我们就需要一个新的数据类型——结构体,它就将不同类型的数据存放在一起,作为一个整体进行处理。
官方来说结构体就是一些值的集合,这些值称为成员变量。结构体的每个成员可以是不同类型的变量。说到集合,数组也是集合,但是不同的是数组只能是相同类型元素的集合。
1. 结构体类型的声明
struct tag
{
member-list;
}variable-list;
*struct是结构体关键字
*tag是结构体的标签名,是自定义的
*struct tag就是结构体类型
*{}里面放的是成员列表
* variable-list是结构体变量*member-list 是结构体成员
例如:描述一位学生
struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
}; //分号不能丢
2. 结构体变量的定义和初始化
2.1结构体的定义
结构体有两种定义方法
#1.先定义结构体类型,再定义结构体变量
struct student //结构体类型 或 结构体名
{
int num;
char name[20]; //结构体成员
char sex;
int age;
float score;
char addr[30];
};
struct student stu1,stu2; //结构体变量
#2.定义结构体类型的同时定义结构体变量
struct data // 结构体类型 或结构体名
{
int day int month; //结构体成员
int year
}time1,time2; //结构体变量
另外,还有一种特殊的定义:
struct // 结构体类型
{
char name[20];
char sex;
int num; //结构体成员
float score[3]
}person1,person2; //结构体变量
省略掉了结构体标签(tag)。
注意:匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使⽤⼀次。
2.2结构体的初始化
和其它类型变量一样,对结构体变量可以在定义时指定初始值。
#1.按照结构体成员顺序初始化
#include <stdio.h>
struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
};
int main()
{
//按照结构体成员的顺序初始化
struct Stu s1 = { "张三", 20, "男", "20230818001" };
printf("name: %s\n", s.name);
printf("age : %d\n", s.age);
printf("sex : %s\n", s.sex);
printf("id : %s\n", s.id);
return 0;
}
#2.按照指定顺序初始化
#include <stdio.h>
struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
};
int main()
{
//按照指定的顺序初始化
struct Stu s2 = { .age = 18, .name = "lisi", .id = "20230818002", .sex ="⼥" };
printf("name: %s\n", s2.name);
printf("age : %d\n", s2.age);
printf("sex : %s\n", s2.sex);
printf("id : %s\n", s2.id);
return 0;
}
3. 结构成员访问操作符
为了访问结构的成员,我们使用成员访问运算符(.)
引用形式:<结构体类型变量名> . <成员名>
注意1:优先级
点其结合性是自左至右的,它在所有的运算符中优先级是最高的;
例如:
s1.title指的就是s1的title部分;
printf(“%s\n%s\n%f”,s1.title,s1.author,s1.value); //访问结构体变量元素
scanf(“%d”,&s1.value); 这语句存在两个运算符,&和结构成员运算符点。
按照道理我们应该将(s1.value括起来,因为他们是整体,表示s1的value部分)但是我们不括起来也是一样的,因为点的优先级要高于&。
如果title是float 型的,s1 虽然是个结构体, 但s1.title 是 float 型的。
注意2:分级访问
如果其成员本身又是一种结构体类型,那么可以通过若干个成员运算符,一级一级的找到最低一级成员再对其进行操作;
结构体变量名.成员.子成员………最低一级子成员;
struct date
{
int year;
int month;
int day;
};
struct student
{
char name[10];
struct date birthday;
}student1;
//若想引用student的出生年月日,可表示为;student.brithday.year;
brithday是student的成员;year是brithday的成员;
注意3:整体与分开
可以将一个结构体变量作为一个整体赋值给另一相同类型的结构体变量,可以到达整体赋值的效果。(这个成员变量的值都将全部整体赋值给另外一个变量)
不能将一个结构体变量作为一个整体进行输入和输出!!
在输入输出结构体数据时,必须分别指明结构体变量的各成员;
总结:除去“相同类型的结构体变量可以相互整体赋值”外,其他情况下,不能整体引用,只能对各个成员分别引用;
注意:结构体是一种自定义的数据类型,是创建变量的模板,不占用内存空间;结构体变量才包含了实实在在的数据,需要内存空间来存储。
4. 结构体内存对齐
4.1 对齐规则
struct S1
{ //自身大小 VS //对齐数
char c1; 1 8 1
int i; 4 8 4
char c2; 1 8 1
};
printf("%d\n", sizeof(struct S1));
struct S2
{
char c1;
char c2;
int i;
};
printf("%d\n", sizeof(struct S2));
struct S3
{
double d;
char c;
int i;
};
printf("%d\n", sizeof(struct S3));
struct S4
{
char c1;
struct S3 s3;
double d;
};
printf("%d\n", sizeof(struct S4));
4.2 为什么存在内存对齐?
总体来说:结构体的内存对齐是拿空间来换取时间的做法。
让占⽤空间⼩的成员尽量集中在⼀起
//例如:
struct S1
{
char c1;
int i;
char c2;
};
struct S2
{
char c1;
char c2;
int i;
};
S1 和 S2 类型的成员⼀模⼀样,但是 S1 和 S2 所占空间的⼤⼩有了⼀些区别。
4.3 修改默认对齐数
#include <stdio.h>
#pragma pack(1)//设置默认对⻬数为1
struct S
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的对⻬数,还原为默认
int main()
{
//输出的结果是什么?
printf("%d\n", sizeof(struct S));
return 0;
}