C语言-自定义类型结构体

📅 2026/8/21 10:37:27
C语言-自定义类型结构体
目录结构体匿名结构体结构体的自引用结构体的内存对齐对齐规则结构体内存对齐的原因结构体传参位段位段的声明位段如何分配内存位段的不确定因素联合体联合体的声明联合体的大小联合体的应用枚举枚举的声明枚举的使用枚举相比于宏定义“#define”的优点结构体结构体生活中一类实物抽象出来的一个定义结构体的定义和结构体变量的创建以及初始化如下#includestdio.h struct Std//结构体的创建 { int age; char name[]; }; int main() { struct Std s1;//结构体变量的创建 s1 {18,zhangsan};//结构体变量的初始化 s1.age 22;//可单独初始化 return 0; }匿名结构体顾名思义没有名字的结构体。一般只能使用一次在代码中的定义如下#includestdio.h struct { int a; char b; }x;//创建匿名结构体变量x结构体的自引用结构体自己引用自己叫做结构体的自引用和二叉树的原理相似在代码中的使用如下#includestdio.h struct Std { int a; struct Std* next;//将下一个同类型的结构体指针作为成员放在结构体中 };结构体的内存对齐对齐规则结构体第一个成员对齐距结构体变量起始位置偏移量为0地址处后续成员对其到某一个对齐数的整数倍地址处对齐数为系统默认对齐数和该成员自身对齐数的较小数VS系统下默认对齐数为8Linux系统没有默认对齐数所以对齐数为成员自身对齐数结构体的整体内存必须是最大对其数的整数倍若存在结构体嵌套的情况嵌套的结构体对齐数为该结构体中最大对齐数最终结构体的内存大小是说有对齐数中最大对齐数的整数倍包含嵌套结构体中的对齐数。PS函数“offsetof类型成员”可以测试出成员地址和结构体起始地址之间的偏移量结构体内存对齐的原因平台原因移植原因不是所有的平台都能从任意地址访问任意数据有一些平台只能从某些地址访问特定类型的数据否则抛出硬件异常。性能原因访问为对齐的内存处理器要进行两次内存访问访问对齐的内存处理器只用仅从一次内存访问。总体来说结构体的内存对齐使用空间来换取运行时间。#includestdio.h//两个成员完全相同顺序不同的结构体因为内存对齐造成结构体的内存大小不同 struct Std1 { char a; int b; char c; }; struct Std2 { char a; char b; int c; }; int main() { printf(%zu\n,sizeof(struct Std1));//12 printf(%zu\n, sizeof(struct Std2));//8 return 0; }如果既要结构体内存对齐又要节省内存空间在定义结构体时把内存较小类型相同的成员放到一起并放到前面Windows系统下在结构体前面加上“#pragma pack()”可以改变编译器的默认对齐数最好改为2的次方数。结构体传参结构体传参分为两种方式结构体变量传参会把结构体变量中的所有成员的值都传过去可莪能会造成压栈现象对时间和内存空间的消耗大结构体指针传参。只用传输结构体的指针最多8个字节传输效率更高。结构体变量传参#includestdio.h struct Std1 { char a; int arr[20]; }; void my_print(struct Std1 s) { s.a 7; printf(%c\n, s.a); } int main() { struct Std1 s1 { 0 }; my_print(s1); return 0; }结构体指针传参#includestdio.h struct Std1 { char a; int arr[20]; }; void my_print(struct Std1* s) { s-a 7; printf(%c\n, s-a); } int main() { struct Std1 s1 { 0 }; my_print(s1); return 0; }位段位段的声明位段的声明的和结构体的声明相似只有两点不同位段中的成员只能是整形家族Int,signed int,unsigned int, char;位段中的成后面跟冒号和一个数字数字表示为这个成员分配的比特位数。struct Std { int a : 2; int b : 5; };这就是一个完整位段的声明。位段如何分配内存位段空间是按照4个字节int或者1个字节char进行开辟的开辟完空间后按照顺序从第一个成员开始存储具体存储规则要看编译器位段涉及到很多不确定因素是不跨平台的注重移植的程序不能使用位段。位段的不确定因素开辟之后的内存空间存储时是从左向右还是从右向左存储先开辟的内存空间不够下一个成员存储时是否舍去剩余的几个比特位int位段是有符号数还是无符号数位段中最大位的数目无法确定16位的机器最大为1632位的机器最大为3216位的机器上给一个位段成员分配了27位就会出问题。PS:与结构体相比位段可以达到相同效果可以节省内存空间但是存在平台问题应用于网络协议中内存的最小单元为1个字节但是在位段中一个字节中可以有几个成员存储因此位段中的成员不能使用取地址符取地址也不能使用scanf()进行输入。联合体联合体像结构体一样可由一个或多个成员构成这些成员可以是不同类型但是编译器只为占内存最大的成员分配足够的内存空间所有的成员共用一个内存空间所以联合体有另一个名字共用体。联合体的声明除了关键字换为union其余声明方式与结构体一致。union Un { int a; char b; };由于联合体的所有成员共用一块内存因此同一时间只能调用一个成员。联合体的大小至少为最大成员的内存大小联合体整体大小必须是最大对齐数的整数倍。union Un { char a[5]; int b; }; int main() { printf(%zu\n, sizeof(union Un));//最大成员内存为5但是最整个联合体的内存为8 return 0; }联合体的应用成员存在同一时间只用一个的情况用于判断系统的大、小端字节序存储和结构体嵌套使用。union Un { char a; int b; }; int main()//VS环境下为小端存储 { union Un un; un.b 1; if (un.a 0) { printf(大端存储); } else { printf(小端存储); } return 0; }枚举把可能的值一一列举。枚举的声明enum SEX { MALE, FEMALE, SECRET };列举的每种情况之间用逗号隔开若没有特殊赋值系统会默认给第一个赋值“0”后续的依次加一枚举的每种情况都有对应的值。声明时可以给每一种情况赋特殊值枚举的使用enum SEX sex MALE;//把其中一种情况赋值枚举相比于宏定义“#define”的优点增加代码的可读性和可维护性枚举有类型检查更加严谨便于调试预处理阶段会删除“#define”定义的符号使用方便依次可以定义多个常量枚举常量遵循作用域规则若在函数内声明则只能在函数内使用。