结构体
什么是结构体
C 语言允许用户自己指定这样一种数据结构,它由不同类型的数据组合成一个整体,这些组合在一个整体中的数据是互相联系的,这样的数据结构称为结构体。
结构体格式
struct 结构体名
{
数据类型 成员变量;
};
定义结构体变量
先声明后定义
struct students
{
char name[32];
char sex;
int age;
int number;
};
struct students students_1;
struct students students_2;
声明的同时定义
struct students
{
char name[32];
char sex;
int age;
int number;
}students_1, students_2;
无名结构体的声明与定义(C11标准)
struct {
int x;
char y;
} z;
typedef struct {
int width;
int height;
} Rect;
struct Data {
int id;
struct {
int x;
int y;
};
};
int main() {
struct Data d;
d.id = 1;
d.x = 10;
d.y = 20;
printf("id=%d, x=%d, y=%d\n", d.id, d.x, d.y);
return 0;
}
结构体嵌套
struct score
{
int math;
int chinese;
}
struct students
{
char name[32];
char sex;
int age;
int number;
struct score stu_score;
}student1, student2;
结构体变量引用
- 不能将一个结构体变量作为一个整体进行输入和输出。只能对结构体变量中的各个成员分别进行输入输出。
- 引用结构体变量中的成员的方式为:
结构体变量名.成员名
struct students student1;
- 如果成员本身又属一个结构体类型,则要用若干个成员运算符,一级一级地找到最低一级的成员。只能对最低的成员进行赋值或存取以及运算。
student1.sex;
student1.stu_score.math;
student2.score = student1.score;
sum = student1.score + student2.score;
student1.age ++;
++ student1.age;
结构体变量初始化
struct score
{
int math;
int chinese;
}
struct students
{
char name[32];
char sex;
int age;
int number;
struct score stu_score;
}student1 = {"LIMING", 'M', 18, 123456, {89, 90}};
struct students student2 = {"LIHUA", 'M', 18, 123457, {81, 95}};
struct students student3={.name="李白",.number=124553, .age=46, .sex='M',\
.stu_score.math = 99, .stu_score.chinese = 99};
结构体数组
- 顾名思义,结构体数组就是存放多个相同结构体类型的数组结构。结构体数组与以前介绍过的数据值型数组不同之处在于每个数组元素都一个结构体类型的数据,它们分别包括各个成员(分量)项。
定义结构体数组
struct students
{
char name[32];
char sex;
int age;
int number;
};
struct students stu[3];
struct student
{
char name[32];
....
}stu[3];
或
struct
{
char name[32];
...
}stu[3];
结构体数组初始化
struct student
{
char name[20];
char sex;
int age;
int number;
}stu[3] = {{"ZhangSan", 'M', 18, 123123},
{"LiSi", 'M', 18, 12312314},
{"WangWu", 'M', 18, 12412412}};
stu[] = {{...},{...},{...}};
结构体指针
- 结构体变量的指针就是该变量所占据的内存段的起始地址,指针变量也可以用来指向结构体数组中的元素。
struct student
{
char name[20];
char sex;
int age;
int number;
};
struct student a = {"李白",'M', 20, 100123};
struct student *p = &a;
p->age = 21;
(*P).age = 21;
(&a)->age = 21;
struct student arr[3] = {{"ZhangSan", 'M', 18, 123123},
{"LiSi", 'M', 18, 12312314},
{"WangWu", 'M', 18, 12412412}};
struct student *p = arr;
arr[i].age = 21;
p[i].gae = 21;
(p + i)->age = 21;
*(p + i).age = 21;
结构体大小及地址
- 在 C 语言中,结构体的大小(即占用的内存空间)和地址分配规则由成员变量的类型、顺序及编译器的内存对齐规则共同决定。
- 结构体的大小并非简单等于所有成员变量大小的总和,而是遵循内存对齐规则,目的是提高 CPU 访问内存的效率。以下是结构体大小计算规则:
-
-
- 对齐基准(Alignment),基本数据类型的对齐基准通常等于其自身大小。结构体成员的对齐要求等于其内部最大成员的对齐基准。
-
-
- 成员地址偏移,每个成员的地址必须是其对齐基准的整数倍。如果当前偏移地址不满足对齐要求,编译器会在前一个成员后插入填充字节(Padding)。
-
-
- 结构体总大小,结构体的总大小必须是其最大成员对齐基准的整数倍。如果最后一个成员后仍有空间不足对齐,编译器会插入填充字节。
- 地址分配规则
-
- 起始地址:结构体变量的地址等于其第一个成员的地址。
-
- 成员顺序:成员按声明顺序依次分配地址,遵循对齐规则。
-
- 嵌套结构体:嵌套的结构体成员的对齐基准是其内部的最大对齐基准。
struct Example1 {
char a;
int b;
short c;
};
Offset 0: char a (1 字节)
Offset 1-3: 填充 3 字节(因为 int b 需要从 4 的倍数地址开始)
Offset 4-7: int b (4 字节)
Offset 8-9: short c (2 字节)
Offset 10-11: 填充 2 字节(总大小需是最大对齐基准 4 的整数倍)
struct Example2 {
int b;
short c;
char a;
};
Offset 0-3: int b (4 字节)
Offset 4-5: short c (2 字节)
Offset 6: char a (1 字节)
Offset 7: 填充 1 字节(总大小需是最大对齐基准 4 的整数倍)
struct Inner {
double d;
int i;
};
struct Outer {
char a;
struct Inner inner;
short s;
};
Offset 0: char a (1 字节)
Offset 1-7: 填充 7 字节(Inner需要从8的倍数地址开始)
Offset 8-15: double d (8 字节)
Offset 16-19: int i (4 字节)
Offset 20-21: short s (2 字节)
Offset 22-23: 填充 2 字节(总大小需是8的整数倍)
结构体大小和成员偏移量的查看
printf("Size: %zu\n", sizeof(struct Example1));
printf("Offset of b: %zu\n", offsetof(struct Example1, b));
补充说明
-
- 类型与变量是不同的概念,只能对变量赋值,存取或运算,而不能对一个类型赋值,存取或运算。在编译时,对类型是不分配空间的,只对变量分配空间。
-
- 对结构体中的成员(即 域)可以单元使用,它的作用与地位相当于普通变量。