自定义类型的介绍及运用

张开发
2026/4/18 14:40:04 15 分钟阅读

分享文章

自定义类型的介绍及运用
目录自定义类型结构体定义结构体创建、声明、初始化及运用结构的特殊声明联合体共用体枚举自定类型计算字节长度struct 结构体结构体的对齐原则​编辑​编辑如何计算结构体所占的字节长度​编辑修改默认对⻬数位段练习union 联合体结构体与共用体的相互运用自定义类型自定义类型是C语言中为了让声明不止局限于整形浮点型字符型而出现的类型自定定义类型更加灵活性。C语言中的自定义类型有结构体、联合体(共用体)和枚举这三种自定义类型。结构体定义结构是⼀些值的集合这些值称为成员变量。结构的每个成员可以是不同类型的变量。struct tag { member-list; }variable-list; //运用 struct stu { char name[20];//名字 int age;//年龄 char id[20];//学号 int tall;//身高 };//注意分号结构体创建、声明、初始化及运用#include stdio.h struct Stu { char name[20];//名字 int age;//年龄 char sex[5];//性别 char id[20];//学号 }; int main() { //按照结构体成员的顺序初始化 struct Stu s { 张三, 20, 男, 20230818001 };//一个中文字符占char是两个 printf(name: %s\n, s.name); printf(age : %d\n, s.age); printf(sex : %s\n, s.sex); printf(id : %s\n, s.id); //按照指定的顺序初始化 struct Stu s2 { .age 18, .name lisi, .id 20230818002, .sex ⼥ }; printf(name: %s\n, s2.name); printf(age : %d\n, s2.age); printf(sex : %s\n, s2.sex); printf(id : %s\n, s2.id); return 0; }struct Stu s { 张三, 20, 男, 20230818001 };这种是按顺序的对应的数值经行初始化也可以单独经行赋值或者初始化要实现这样的操作就需要这个“.”在观察代码的时候你也已经注意到了s.name这样的写法这种就是想要使用结构体单独某一个数值需要的操作。结构的特殊声明//匿名结构体类型 struct { int a; char b; float c; }x;这种的声明方式只能使用一次相对于只有一个x 可以用如果想要再次声明一个一模一样的不同名字的结构体不行。struct X { int a; char b; float c; }x;这种的声明方式就在原本的基础上又声明了一个全局变量我们想要继续声明一个一模一样的不同名字的结构体是可以的。注意点struct { int a; char b; float c; }x; struct { int a; char b; float c; } *p; p x;//error要相同的结构体用的是同一个结构体声明出来的变量和指针才可以相互对应即使是两个结构体的内容一模一样也不可以简单来说只认一个妈生出来的。注意这种在联合体union都是适用的。联合体共用体联合体和共用体其实都是同一个东西只是说有不同的教材有不同的叫法但总归都指向的都是union 这个单词。无论是共用体还是联合体都指向公用这个重要的词上那么他与结构体有什么区别呢struct S { int a; char b; float c; }; union O { int a; char b; float c; };在相同的代码情况下他们内存的存取都有本质区别。struct 结构体存取变量都是有让每一个变量都有位置可以存取而union 的联合体把所有的变量首地址对准第一个位置。所有union 联合体如果选择看其中的一个的变量进行存取其他的变量就没办法存取如果存取会导致存取会导致内存内容丢失或者错乱。struct 和union 的优势和劣势对比struct 结构体优势能够保证么一个变量有足够的空间存取内容。劣势如果有点变量没必要存取的时候就会导致内存的浪费。union 联合体优势能够选择性的存取内容极大的节省了空间的占用。劣势不能存取多大其他的变量。枚举枚举虽然他的编写方式与结构体联合体一样但是他的可能说与#define 定义更加类似。在一次使用#define min 0,最多只能一个一个的定义然后给对应的赋值但是在枚举就可以省略掉大部分的工作enum 可以一次性定义大量的常量。enum Day { mon, tues, wen, thur, fir, sat, sun };这些可能取值都是有值的默认从0开始依次递增1当然在声明枚举类型的时候也可以赋初值。例如mon 0wen2我们也可以让mon不是0可以是其他值。enum Day { mon1; tues, wen, thur, fir, sat, sun };注意如果我们对上面的值mon1在往后的数值也会跟这改变前提没有赋值的数值例如tues2wen3…… 以此类推所以即使mon1默认从0开始依次递增1这个定理没有改变只是开始的值改变了。enum Day { mon1; tues, wen, thur1, fir, sat, sun };这样的数值对应1 2 3 1 2 3 4输出结果总结枚举的优点1.增加代码的可读性和可维护性2.和#define定义的标识符⽐较枚举有类型检查更加严谨。3.便于调试预处理阶段会删除#define定义的符号4.使⽤⽅便⼀次可以定义多个常量5.枚举常量是遵循作⽤域规则的枚举声明在函数内只能在函数内使⽤自定类型计算字节长度struct 结构体案例展示struct A { int a; char b; char c; }; int main() { printf(%d, sizeof(struct A));//8 return 0; }输出结果 8struct A { char a; int b; char c; }; int main() { printf(%d, sizeof(struct A));//12 return 0; }输出结果12为什么两个结构体声明类型数量一样但是循序排列不一样就会出现占的字节不一样的结果。那么就得说到结果结构体的对齐原则。结构体的对齐原则为什么存在对齐因为在不同的编译器里都有不同情况如果像4个字节1字节5个字节会导致信息处理混乱。比较官方的说法是不是所有的硬件平台都能访问任意地址上的任意数据的某些硬件平台只能在某些地址处取某些特定 类型的数据否则抛出硬件异常。数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于为了访问未对⻬的内存处理器需要作两次内存访问⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数那么就可以⽤⼀个内存操作来读或者写值了。否则我们可能需要执⾏两次内存访问因为对象可能被分放在两个8字节内存块中。各个声明的所占的字节长度展示char short int long long long float double long double 所长字节1 2 4 4 8 4 8 8指针统一在x86环境下是以4给字节为基础在x64 环境下是以 8个字节为基础。如何计算结构体所占的字节长度struct A1 { char a; char b; int c; }; struct A2 { char a; int b; char c; };其实计算结构体就像跟放一个有网状的纸箱盒子一样我们把每个变量比作物品每个声明变量所占字节的大小就是物品的大小大的物品就放进放进大一点的网格纸箱里计算机比较死板只能按顺序放进我们准备好的顺序方进去。例如以下的结构体。struct A1 { char a; char b; int c; };结果是8那我们还是把他看成装箱子来看带入计算机的视角看到代码第一步就是先看到char 占一个字节那么我们就会直接去那一个以一个字节为大小的网格如何我们按顺序把char a 和char b 放进去。但是我们遇到int 占4个字节 那么我们就应该去用更大号的网格指向来装它。把之前的内容装进去并且把int c 装进去所以所占的字节数就是2*48个字节。struct A2 { char a; int b; char c; };结果12总结尽量把相同变量名称排在一起避免内存浪费。修改默认对⻬数那么其实计算机还是比较智能一点的它会先查找结构体占的最大的声明变量来看是用多大网格的纸箱网格的大小我们用的专业术语叫对齐数。在理论上int long 等声明变量的最大数值为8 那么默认对齐数最大对齐数也是8但是不同的编译器还是有不同的情况我们也可以同跟更改对齐数来适配字节需要的对齐数。#pragma pack(1)//默认对齐数为1 struct A { char a[5]; int b; }; #pragma pack()//修改为原始的对齐数VS 默认是8 int main() { printf(%d, sizeof(struct A));//9 return 0; }结构体在对⻬⽅式不合适的时候我们可以⾃⼰更改默认对⻬数。位段位段的计算与结构体计算类似依然还是纸箱放东西但是位段的只能有signedint unsigned int signedchar unsigned char 这几个位段就比平常的计算分得更细char 占一个字节一个字节又占了8个bit 为所以我们就是针对bit位来申请大小的。char 位段计算规律代码展示struct s { char _a : 3; char _b : 4; char _c : 5; char _d : 4; }; int main() { printf(%d, sizeof(struct s));//3 return 0; }通过上图代码 char类型生成1个字节 00000000char _a : 3;占3个bit位char _b : 4;占4个bit char _c : 5;因为只剩1个bit位所以在生成一个字节的位置但是剩下一个bit位不会用会直接跳过char _d : 4;同理最终结果占3个字节。int 位段计算规律代码展示struct s { int _a : 3; int _b : 4; int _c : 5; int _d : 4; }; int main() { printf(%d, sizeof(struct s));//4 return 0; }通过上图代码 char类型生成4个字节 00000000 00000000 00000000 0000000032个bit位因为32个bit位足够充足所以这些int _a : 3;int _b : 4;int _c : 5;int _d : 4;总共16给bit但是剩余的16个bit 位依然还是会被记录在内所以占4个字节。int 和char 的混合使用代码展示struct s { char _a : 3; int _b : 4; char _c : 5; char _d : 4; }; int main() { printf(%d, sizeof(struct s));//12 return 0; }计算机会先找最大的对齐数int 4所以先生成4个字节32个bit位存入char _a : 3;要符合对齐原则所以int 再一次生成了4个字节存入 int _b : 4;同理再一次生成4个字节存入 char _c : 5; char _d : 4;所以结果占12个字节。总结最好在时候位段的时候要统一是要用int 还是char 避免内存的浪费。练习struct A { char a[5]; int b; }; struct B { char a[3]; int b; long long c; }; struct C { char a[5]; int b; short c; long long d; };输出结果12 16 24struct C {char a[5];int b;short c;long long d;};计算方式char 直接加 5 目前5int 4对准4的倍数5384 12目前12short 对准2的倍数12 刚好是2的倍数不用加目前14long long 对准8 的倍数142168目前24结果24.union 联合体联合的计算法就比结构体简单多了只需要计算算出里面最大的所占空间的变量就可以。#include stdio.h union Un1 { char c[5]; int i; }; union Un2 { short c[7]; int i; }; int main() { //下⾯输出的结果是什么 printf(%d\n, sizeof(union Un1));//5 printf(%d\n, sizeof(union Un2));//14 return 0; }无论是结构体内加联合体还是联合体内嵌套结构体默认对齐数都是以以VS编译器为例子8为为准。计算Arr 是所占字节struct Arr{ int p; union { struct { char a[5]; int b; }A; struct { char a[3]; int b; long long c; }B; struct { char a[5]; int b; short c; long long d; }C; }str; };因为union str 所占字节为24但最大对齐数为8因为里面有long long所以是8所以 int 4 要以8对齐4对齐824最终结果32。结构体与共用体的相互运用使⽤联合体是可以节省空间的举例⽐如我们要搞⼀个活动要上线⼀个礼品兑换单礼品兑换单中有三种商品图书、杯⼦、衬衫。每⼀种商品都有库存量、价格、商品类型和商品类型相关的其他信息。图书书名、作者、⻚数杯⼦设计衬衫设计、可选颜⾊、可选尺⼨那我们不耐⼼思考直接写出⼀下结构struct gift_list { //公共属性 int stock_number;//库存量 double price; //定价 int item_type;//商品类型 //特殊属性 char title[20];//书名 char author[20];//作者 int num_pages;//⻚数 char design[30];//设计 int colors;//颜⾊ int sizes;//尺⼨ };上述的结构其实设计的很简单⽤起来也⽅便但是结构的设计中包含了所有礼品的各种属性这样 使得结构体的⼤⼩就会偏⼤⽐较浪费内存。因为对于礼品兑换单中的商品来说只有部分属性信息 是常⽤的。⽐如商品是图书就不需要design、colors、sizes。所以我们就可以把公共属性单独写出来剩余属于各种商品本⾝的属性使⽤联合体起来这样就可以 介绍所需的内存空间⼀定程度上节省了内存。struct gift_list { int stock_number;//库存量 double price; //定价 int item_type;//商品类型 union { struct { char title[20];//书名 char author[20];//作者 int num_pages;//⻚数 }book; struct { char design[30];//设计 }mug; struct { char design[30];//设计 int colors;//颜⾊ int sizes;//尺⼨ }shirt; }item;感谢观看悠仁さん

更多文章