一、结构体
1.1什么是结构体
C语言允许用户自己建立由不同类型数据组成的组合型的数据结构,它称为结构体(structre)。也可以称为"记录"(record)。
1.2定义结构体类型变量
struct是声明结构体类型时必须使用的关键字,不能省略。
声明一个结构体类型的一般形式为:
cpp
struct 结构体名
{
成员列表
};
结构体名是由用户规定的,也可以称为"结构体标记"(structure tag),以区别于其它结构体类型。
花括号内时该结构体所包括的子项,称为结构体的成员(member)。
对各成员都应该进行类型声明,即:类型名 成员名;
定义结构体类型的三种方法
1.先声明结构体类型,再定义该类型的变量
cpp
#include<stdio.h>
struct Student
{
int num;//学号
char name[20];//姓名
int age;//年龄
float score;//成绩
};
int main()
{
struct Student student1, student2;//声明了两个结构体变量为student1,student2
return 0;
}
这种方法是声明类型和定义变量分离,在声明类型后可以随时定义变量,较为灵活。
2.在声明类型的同时定义变量
这种定义方法的一般形式为:
cpp
struct 结构体名
{
成员列表
}变量名列表;
举例:
cpp
#include<stdio.h>
struct Student
{
int num;//学号
char name[20];//姓名
int age;//年龄
float score;//成绩
}student1,student2;
3.不指定类型名而直接定义结构体类型变量
这种定义方法的一般形式为:
cpp
struct
{
成员列表
}变量名列表;
在定义类型时不指定结构体名,也可以称这种定义方式为匿名 。
举例:
cpp
#include<stdio.h>
struct
{
int num;//学号
char name[20];//姓名
int age;//年龄
float score;//成绩
}student1, student2;
1.3结构体变量的赋值
1. 按照结构体成员的顺序初始化
cpp
#include<stdio.h>
struct Student
{
int num;//学号
char name[20];//姓名
char sex[20];//性别
int age;//年龄
float score;//成绩
};
int main()
{
struct Student student1 = { 12138,"Lin","man",20,99 };
printf("num:%d\nname:%s\nsex:%s\nage:%d\nscore:%d\n", student1.num, student1.name, student1.sex, student1.age, student1.score);
return 0;
}
2.按照指定的顺序进行初始化
cpp
#include<stdio.h>
struct Student
{
int num;//学号
char name[20];//姓名
char sex[20];//性别
int age;//年龄
float score;//成绩
};
int main()
{
struct Student student1 = { .age = 18,.num = 12138,.sex = "man",.score = 99,.name = "Lin" };
printf("num:%d\nname:%s\nsex:%s\nage:%d\nscore:%d\n", student1.num, student1.name, student1.sex, student1.age, student1.score);
return 0;
}
3.单独对结构体中的某个成员赋值
cpp
#include<stdio.h>
#include<string.h>
struct Student
{
int num;//学号
char name[20];//姓名
char sex[20];//性别
int age;//年龄
float score;//成绩
};
int main()
{
struct Student student1, student2;
//对变量的age赋值
student1.age = 18;
//或
scanf("%d", &student2.age);
//对student2的name赋值
gets(student1.name);
//或
for (int i = 0; i < 20; i++)
{
scanf("%c", &student2.name[i]);
if (i == 19)
{
student2.name[i] = '\0';
}
}
//输出
printf("%d\n", student1.age);
printf("%d\n", student2.age);
printf("%s\n", student1.name);
printf("%s\n", student2.name);
return 0;
}
在单独对结构体的数组成员赋值时,需要使用循环输入。
1.4内存对齐
结构体内存对齐是指编译器在分配结构体内存时,会按照特定的规则来对齐每个成员的地址,以提高内存访问效率和系统性能。内存对齐主要涉及两个方面:对齐要求和填充字节。
1. 对齐要求
每种数据类型都有其特定的对齐要求,这通常由数据类型的大小决定。例如:
char
类型的对齐要求是 1 字节。short
类型的对齐要求是 2 字节。int
和float
类型的对齐要求是 4 字节。double
类型的对齐要求是 8 字节。
2. 填充字节
为了满足对齐要求,编译器可能会在结构体成员之间或结构体末尾插入额外的字节,这些字节称为填充字节(padding bytes)。这些填充字节确保每个成员的地址都是其对齐要求的倍数。
3.对齐的实际规则
- 成员对齐:每个成员的地址必须是该成员大小的整数倍。
- 结构体对齐:结构体的总大小必须是最大对齐要求的成员大小的整数倍。
4.影响结构体对齐的因素
- 编译器:不同编译器对结构体内存对齐的处理可能有所不同。
- 硬件平台:不同的处理器架构可能对内存对齐有不同的要求。
- 编译选项:有些编译器允许通过选项指定内存对齐方式。
5.VS2022平台的结构体内存对齐
在VS2022平台上,默认的对齐数为8个字节,如果结构体的某个成员所需要的字节数比默认字节数小,则选取小的作为对齐数。
结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的位置,其它成员要对齐到对齐数的整数倍的位置。
结构体每个成员变量都有一个对齐数,所有对齐数中最大的即为最大对齐数。
举例:
在上图中:
结构体成员a的类型为short,大小为2个字节,是结构体成员列表中的第一个成员,因此在内存中存放在起始位置偏移量为0的地方;
结构体成员d的类型为char,大小为1个字节,和VS默认对齐数8相较为小,因此对齐数为1,在内存中要存放到对齐数的整数倍位置,也就是偏移量为2的地方;
结构体成员b的类型为long,在x86环境中为4个字节, 和VS默认对齐数8相较为小,因此对齐数为4,在内存中偏移量为3的地方不是4的倍数,因此要浪费一个字节再继续存放;
结构体成员c的类型为long,大小为4个字节,偏移量为8的地方正好是4的倍数,因此可以存放无需浪费;
最大对齐数为4,所以该结构体类型的大小也必须为最大对齐数的倍数,也就是4的倍数,从上如可以看出,存放完所有结构体成员后共占了12个字节,是4的倍数。
综上所述,该结构体类型所占大小为12个字节。