结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。
参考:初识结构体
结构的声明
12345 | struct tag{ member-list; }variable-list; |
例如描述一个学生,声明一个结构体类型,通过学生类型来创建学生变量:
——姓名、电话、性别、年龄
12345678910111213141516 | struct Stu{ char name[20]; char tele[12]; char sex[5]; int age;}s4,s5,s6;//s4,s5,s6和s3一样,都是全局的结构体变量 struct Stu s3;//创建了s3这个结构体的全局变量 int main() { struct Stu s1; struct Stu s2;//创建了s1,s2两种结构体变量(局部变量) return 0; } |
特殊的声明
匿名结构体类型
12345678910111213 | struct{ int a; char b; float c;}x;//创建匿名结构体类型时,必须在后面写上结构体类型变量,否则之后无法创建 struct { int a; char b; float c;}a[20],*p;//*p匿名结构体指针 |
结构体自引用
自引用方式
12345 | struct Node{ int data; struct Node* next;}; |
123456789101112 | typedef struct Node{ int data; struct Node* next;}Node; int main(){ struct Node n1; Node n2; return 0;} |
结构体变量的定义和初始化
123456 | struct Point{ int x; int y;}p1;//声明类型的同时定义变量p1struct Point p2;//定义结构体变量p2 |
定义变量同时赋值:
12345678910111213 | struct S{ char c; int a; double d; char arr[20];}; int main() { struct S s = {'c',100,3.14,"hello world"};//结构体成员初始化 printf("%c,%d,%lf,%s\n",s.c,s.a,s.d,s.arr);//结构体成员的访问 return 0;} |
打印的结果是:c 100 3.140000 hello world
结构体成员的嵌套
123456789101112131415161718192021 | struct T{ double weight; short age;}; struct S{ char c; struct T st; int a; double d; char arr[50];}; int main() { struct S s = {'c',{55.4,30},100,3.14,"hello world"}; printf("%lf\n",s.st.weight);//访问嵌套的结构体变量 return 0;} |
打印的结果是55.400000
嵌套的结构体变量就是在大括号里加大括号
结构体内存对齐
12345678910111213141516171819202122 | struct S1{ char c1; int a; char c2;}; struct S2{ char c1; char c2; int a;}; int main(){ struct S1 s1 = {0}; printf("%d\n",sizeof(s1));//12 struct S2 s2 = {0}; printf("%d\n",sizeof(s2));//8} |
如何计算结构体大小?
——对齐规则
1、第一个成员在与结构体变量偏移量为0的地址处
2、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值
3、结构体的总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
4、如果嵌套了结构体的情况,嵌套结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
为什么要内存对齐?
——性能强,兼容性强
修改默认对齐数
使用#pragma()指令来修改默认对齐数。
123456789101112131415 | struct S{ char c1;//1 //7 double d;//8};int main(){ struct S s; printf("%d\n",sizeof(s)); return 0;} |
默认的对齐数是8,#pragma pack(4)修改默认对齐数为4,使原先的结构体s的大小由16变成了12。(中间浪费的字节由8变成了3)
偏移量计算offsetof
size_t offsetof(structName, memberName);
用于计算偏移量,它不是函数,是一个宏。
12345678910111213141516 | struct S{ char c; int i; double d;}; int main(){ printf("%d ",offsetof(struct S, c)); printf("%d ",offsetof(struct S, i)); printf("%d ",offsetof(struct S, d)); return 0;} |
打印的结果是0 4 8
结构体传参
结构体传参,可以传地址,也可以传本身。
123456789101112131415161718192021222324252627282930313233 | struct S{ int a; char c; double d;}; void Init(struct S* ps){ ps->a = 100; ps->c = 'w'; ps->d = 3.14;} void print1(struct S tmp){ printf("%d %c %lf\n",tmp.a,tmp.c,tmp.d);} void print2(const struct S* ps){ printf("%d %c %lf\n",ps->a,ps->c,ps->d);} int main(){ struct S s = {0}; Init(&s); print1(s);//传值 print2(&s);//传址 return 0;} |
结构体传参,最好传地址,因为传的值有可能过大,系统开销大,通过传地址,可以节约系统开销。