结构体:用一些变量描述个体
结构:一些值的集合,统称为成员变量,结构体中的成员变量可以是任意类型的变量,标量、数组、指针、 结构体,成员变量描述属性
创建结构体并初始化:
struct tag
{
member-list; -- 成员列表
}variable-list; -- 变量列表
定义结构体类型,--图纸,不会占用内存空间;创建变量,--房子,占用内存空间
// 声明结构体变量
struct S
{
char name[20];
int age;
char sex[5];
char tele[12];
};
// 创建并初始化结构体
struct S s1 = { "zhangsan", 20, "nan", "12345677910" };
结构体作为函数参数进行传参:
1. 传结构体变量,利用结构体变量接收
2. 传结构体地址,利用结构体指针接收
#include <stdio.h>
struct S
{
char name[10];
int age;
};
void print1(struct S s)
{
printf("%s\n", s.name);
printf("%d\n", s.age);
}
void print2(struct S* s)
{
printf("%s\n", s->name);
printf("%d\n", s->age);
}
int main()
{
struct S s1 = { "zhangsan", 20 };
struct S s2 = { "lisi", 30 };
print1(s1); // 传结构体变量
printf(&s2);// 传结构体指针
return 0;
}
相对于传结构体变量本身,对程序来说传结构体指针更好。当结构体作为参数足够大时,参数是需要压栈的,参数压栈的系统开销会很大;而结构体指针仅仅传地址,即4或8个字节
对结构体类型重命名:typedef
typedef struct S
{
char name[5];
int age;
}student;
// 将struct S重命名为student
匿名结构体:省略掉结构体中的tag标签,定义的结构体变量
通过匿名结构体只能创建全局变量,不能创建局部变量
即使两个匿名结构体中的成员变量完全相同,但是编译器仍然会把两个结构体声明当成不同的类型
struct
{
char name[5];
int age;
char tele[12];
}s1; // 创建匿名结构体变量s1
struct
{
char name[5];
int age;
char tele[12];
}s2; // 创建匿名结构体变量s2
int main()
{
// struct s; 不成立的,匿名变量无法创建局部变量
return 0;
}
结构体的自引用:
结构体的自引用通过指针实现,成员变量中含有指针,指针指向下一个相同的结构体变量;
结构体中包含一个类型为该结构体本身的成员--不成立,无限套娃,无法确定该结构体所占内存的大小
结构体与内存对齐:
存放规则:注:对齐数,编译其默认的对齐数与其数据类型大小的较小值,VS编译器默认是8
1.结构体中的第一个成员在结构体偏移量为0的地方
2.其他的成员对齐到某个数字(对齐数)的整数倍地址处
3.结构体最终大小为最大对齐数(每个成员变量都有对齐数)的整数倍
4.结构体嵌套结构体时,嵌套结构体对齐到自己最大对齐数的整数倍处,结构体的最大对齐数是成员变量的最大对齐数
结构体的内存设计方便从内存中取数据,是拿空间换时间的做法
设计结构体时,满足对齐的同时尽可能节省空间的做法:占用空间小的成员尽量集中到一起
修改结构体的对齐数:pragma语句
#pragma pack(4) // 修改默认对齐数为4
struct S
{
char c; // 1
// 浪费3个字节--偏移量为4时存放
int a; // 4
}
// 1+3+4 = 8
#pragma pack() // 取消默认对齐数
offsetof:计算结构体的偏移量,头文件为<stddef.h>
函数:size_t num offsetof(structName, memberName)