自定义类型【C进阶】
创始人
2025-05-31 23:10:34

文章目录

  • 😺结构体
    • 🟥结构体内存对齐
      • 内存对齐的规则
      • 内存对齐的意义
    • 修改默认对齐数
  • 😺位段
    • 🟥位段的内存分配
    • 位段的跨平台性
    • 位段的应用
  • 😺枚举
    • 枚举类型的定义
    • 🟥枚举常量的值
    • 枚举的优点
  • 😺联合(共用体)
    • 联合的初始化
    • 🟥联合的大小
    • 联合求解大小端

😺结构体

在结构体初阶中我们介绍过了结构体基础知识
我们现在来复习一下关于结构体的基础知识

  1. 结构体的声明包括了结构体标签、结构体成员列表
  2. 结构体可以匿名定义,如果是匿名定义的结构体只能在定义结构体时定义结构体变量,如strucrt {int a; int b }s1; ,并且若结构体是匿名的,后续即使出现拥有相同结构成员的结构体也不会被当作一样的结构体,一般不推荐使用匿名结构体
  3. 结构体的初始化需要加上{},可以不按成员顺序进行初始化,这样在初始化列表中需要指定成员初始化如{.member = ?}
  4. 结构体定义时自引用不能自引用结构体本身类型成员,只能自引用结构体指针成员如
    struct S{struct S* a}而不能struct S{struct S a}
  5. 使用typedef重定义结构体时注意不要将结构体成员定义为typedef后的类型名如typedef struct{S a}S;此时编译器不知道S是什么样子的类型
  6. 访问结构体成员时有直接访问和间接访问两种方式,直接访问通过结构体变量名.成员进行访问,间接访问通过结构体指针->成员进行访问
  7. 结构体传参时为了节省在栈区给参数开辟的空间大小,最好传结构体指针

🟥结构体内存对齐

在了解结构体的基本使用后,我们来思考一个问题,如何计算结构体的大小?
这就涉及到了结构体内存对齐
先来看一个例子

struct S
{char c1;int i;char c2;
};
int main()
{printf("%d\n", sizeof(struct S));
}

在这里插入图片描述
😲答案居然是12,是不是和你想的亿点点不一样?
既然涉及到了内存对齐,那结构体的大小肯定不能按正常来计算啦

内存对齐的规则

以下是结构体内存对齐的规则

  1. 第一个成员在与结构体变量偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    对齐数 = 编译器默认的一个对齐数与该成员大小较小值
    VS中默认的值为8,Gcc没有默认对齐数
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

再来分析一下这个题
在这里插入图片描述

练习2.
struct S2
{char c1;char c2;int i;
};
int main()
{printf("%d\n", sizeof(struct S2));
}

在这里插入图片描述

练习3.
struct S3
{double d;char c;int i;
};
int main()
{printf("%d\n", sizeof(struct S3));
}

在这里插入图片描述

练习4.
struct S3
{double d;char c;int i;
};struct S4
{char c1;struct S3 s3;double d;
};
int main()
{printf("%d\n", sizeof(struct S4));
}

在这里插入图片描述
注意第4条规则:嵌套结构体的对齐数是该结构体的最大对齐数整数倍

tips:我们可以通过宏offsetof来求出结构体各个成员的偏移量,引入头文件stddef.h
在这里插入图片描述
在这里插入图片描述

内存对齐的意义

平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常
性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问

在这里插入图片描述

tips:在设计结构体时有一个小技巧可以让结构体占的空间小,那就是将占据字节数小的成员放在前面
在这里插入图片描述

修改默认对齐数

使用预处理指令#pragma pack()修改默认对齐数
在VS环境下,默认对齐数是8,我们有时可以按照自己的需要进行修改,当默认对齐数为1时,表示不存在内存对齐

在这里插入图片描述
若不修改默认对齐数,则c2的偏移量是8,结构体大小是16
结论:当默认对齐数不合适时,我们可以自己修改

😺位段

位段(或称“位域”,Bit field)为一种数据结构,可以把数据以位元的形式紧凑的储存,并允许程序员对此结构的位元进行操作。这种数据结构的好处:

  1. 可以使数据单元节省储存空间,当程序需要成千上万个数据单元时,这种方法就显得尤为重要。
  2. 位段可以很方便的访问一个整数值的部分内容从而可以简化程序源代码

而位域这种数据结构的缺点在于,其内存分配与内存对齐的实现方式依赖于具体的机器和系统,在不同的平台可能有不同的结果,这导致了位段在本质上是不可移植的

在这里插入图片描述

注:

  • 不可以对位段成员进行取地址
  • 位段成员不可以是静态的
  • 位段成员不可以是数组

🟥位段的内存分配

  1. 位段的成员可以是整形家族和枚举类型
  2. 位段的空间上是按照需要==以4个字节( int )或者1个字节( char )==的方式来开辟的。
  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
//一个例子
struct S
{char a : 3;char b : 4;char c : 5;char d : 4;
};
int main()
{struct S s = { 0 };s.a = 10;s.b = 12;s.c = 3;s.d = 4;//空间是如何开辟的?
}

关于这个问题,我们先假设两点

  1. 每一个位段成员使用内存时是从分配的字节右边开始使用
  2. 当分配的字节中剩余的比特位不够容纳下一个位段成员时,抛弃该字节剩下的比特位,重新申请一个新的字节
    在这里插入图片描述
    发现和我们假设的出来的结论一样,但是请记住:不是所有的机器都是和我们假设的一样的

位段的跨平台性

  1. int 位段被当成有符号数还是无符号数是不确定的。
  2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机
    器会出问题。
  3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
  4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是
    舍弃剩余的位还是利用,这是不确定的

总结
跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台问题的存在

位段的应用

在这里插入图片描述

😺枚举

枚举顾名思义就是一一列举
把可能的取值一一列举

比如我们现实生活中:
一周的星期一到星期日是有限的7天,可以一一列举。
性别有:男、女、保密,也可以一一列举。
月份有12个月,也可以一一列举
这里就可以使用枚举了
枚举本质上是int类型,可以在使用int时使用枚举

枚举类型的定义

enum Day//星期
{Mon,Tues,Wed,Thur,Fri,Sat,Sun
};
enum Sex//性别
{MALE,FEMALE,SECRET
};
enum Color//颜色
{RED,GREEN,BLUE
};

上述中的enum Day\enum Sex\enum Color都是枚举类型,{}中的是一些枚举的可能取值称为枚举常量

enum spectrum
{red,orange,yellow,green,blue,violet
}color;int main()
{int c;color = blue;if (color == yellow){;}for (color = red; color <= violet; color++){;}return 0;
}

虽然枚举符(如 red 和 blue)是 int 类型,但是举变量可以是任意整数类型,前提是该整数类型可以储存枚举常量。例如,spectrum 的枚举符范围是0~5,所以编译器可以用unsigned char 来表示color 变量。
顺带一提,C 枚举的一些特性并不适用于 C++。例如,C 允许枚举变量使用++运算符,但是 C++标准不允许。所以,如果编写的代码将来会并入 C++程序,那么必须把上面例子中的 color 声明为 nt 类型才能C和 C++都兼容。

在声明数组时可以将枚举常量作为数组元素个数,也可以将枚举常量用作case语句标签

🟥枚举常量的值

默认情况下,枚举列表中的常量都被赋予 0、1、2等。因此,下面的声明中 nina 的值是 3:

enum kids {nippy, slats, skippy, nina, liz);

如果只给一个枚举常量赋值,没有对后面的枚举常量赋值,那么后面的常量会被赋予后续的值。例如,假设有如下的声明:

enum feline (cat,lynx = 10,puma,tiger};

cat 的值是0(默认),lynx、puma 和 tiger 的值分别是 10、11、12。

枚举的优点

  1. 增加代码的可读性和可维护性
  2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
  3. 防止了命名污染(封装)
  4. 便于调试
  5. 使用方便,一次可以定义多个常量

😺联合(共用体)

联合(union)是一种数据类型,它能在同一个内存空间中储存不同的数据类型(不是同时储存)。其典型的用法是,设计一种表以储存既无规律、事先也不知道顺序的混合类型。使用联合类型的数组,其中的联合都大小相等,每个联合可以储存各种数据类型。

创建联合和创建结构的方式相同,需要一个联合模板和联合变量。可以用一个步骤定义联合,也可以用联合标记分两步定义。下面是一个带标记的联合模板:

union hold 
{
int digit;
double bigfl;
char letter;
}

根据以上形式声明的结构可以储存一个 int 类型、一个 double 类型和 char 类型的值。然而,声明的联合只能储存一个 int 类型的值或一个 double 类型的值或 char 类型的值。

联合的初始化

可以初始化联合。需要注意的是,联合只能储存一个值,这与结构不同。有 3 种初始化的方法:把个联合初始化为另一个同类型的联合: 初始化联合的第 1 个元素:或者根据 C99 标准,使用指定初始化器

union hold valA;
valA.letter =R';
union hold valB = valA;//用另一个联合来初始化
union hold valC = (88};// 初始化联合的 digit 成员
union hold valD = {.bigfl = 118.2};// 指定初始化器

🟥联合的大小

  • 联合的大小至少是最大成员的大小。
  • 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍
//最大对齐数4
union Un1
{char c[5];//对齐数1int i;//对齐数4
};//最大对齐数4
union Un2
{short c[7];//对齐数2int i;//对齐数4
};
int main()
{//下面输出的结果是什么?printf("%d\n", sizeof(union Un1));//8printf("%d\n", sizeof(union Un2));//16
}

联合求解大小端

//验证大小端
union check
{char ch;int i;
}a;int main()
{a.i = 1;if (a.ch == 1) printf("小端\n");else  printf("大端\n");}

在这里插入图片描述

相关内容

热门资讯

番禺有哪些公园好玩的,广州市番... 番禺有哪些公园好玩的目录番禺有哪些公园好玩的广州市番禺区有哪些公园广州番禺有哪些公园好玩番禺旅游必去...
研究生读几年研究生的介绍,研究... 研究生读几年研究生的介绍目录研究生读几年研究生的介绍研究生读几年学制研究生是啥啊?国内研究生读几年研...
擦什么可以让纹身变淡,抹点什么... 擦什么可以让纹身变淡目录擦什么可以让纹身变淡抹点什么可以淡化纹身什么可以淡纹身不花钱去掉纹身的小窍门...
61键电子琴各个琴键代表什么,... 61键电子琴各个琴键代表什么目录61键电子琴各个琴键代表什么如何认识61键电子琴电子琴上的按钮分别是...
汽车钣金是什么意思(汽车钣金是... 今天给各位分享汽车钣金是什么意思的知识,其中也会对汽车钣金是什么意思的照片进行解释,如果能碰巧解决你...
40尺的高柜集装箱尺寸是多少(... 本篇文章极速百科给大家谈谈40尺的高柜集装箱尺寸是多少,以及40尺的高柜集装箱尺寸是多少寸对应的知识...
斗罗大陆唐三的魂环分别是什么,... 斗罗大陆唐三的魂环分别是什么目录斗罗大陆唐三的魂环分别是什么唐家三少的《斗罗大陆》一书中,主角唐三的...
浅色硅胶手机壳脏了怎么清洗,硅... 1. 准备工具:洗洁精、牙膏、牙刷、风油精、棉签、橡皮擦和抹布。 以上内容仅供参考,可阅读硅胶...
英雄联盟lng是哪个战队,ln... 英雄联盟lng是哪个战队目录英雄联盟lng是哪个战队lng是哪个国家的战队lng打野tarzan哪国...
有没有学习美妆的软件,有什么教... 有没有学习美妆的软件目录有没有学习美妆的软件有什么教化妆的app?有什么美妆的APP有什么教化妆的a...
黑芝麻一天吃多少合适,每天吃多... 黑芝麻一天吃多少合适目录黑芝麻一天吃多少合适每天吃多少黑芝麻为宜黑芝麻每天吃多少合适?黑芝麻每天吃多...
东风标志307三厢车多长(东风... 今天给各位分享东风标志307三厢车多长的知识,其中也会对东风标志307三厢车多长多宽进行解释,如果能...
法国百科法国女人最钟爱的香水是... 本篇文章极速百科给大家谈谈法国百科法国女人最钟爱的香水是?,以及法国的什么香水最有名对应的知识点,希...
自然灾难电影有哪些 极速百科网... 自然灾难电影有哪些目录自然灾难电影有哪些自然灾难电影有哪些自然灾难片都有那些?像(后天)(烈火雄心)...
免费高速收费2023最新规定,... 今天给各位分享免费高速收费2023最新规定,高速费免费时间2023的知识,其中也会对202年高速路免...
紫砂壶水温高时会渗水是为什么(... 本篇文章极速百科给大家谈谈紫砂壶水温高时会渗水是为什么,以及紫砂壶烧成温度过高会影响透气性吗对应的知...
陕西省三原县属于哪个市,三原县... 陕西省三原县属于哪个市目录陕西省三原县属于哪个市三原县是哪个市的三原县是属于西安还是咸阳?三原邮编陕...
莫扎特的作品有哪些,莫扎特的名... 莫扎特的作品有哪些目录莫扎特的作品有哪些莫扎特的名曲有哪些?莫扎特有什么曲?莫扎特的作品有哪些?莫扎...
三字词语有哪些,三字词语 极速... 三字词语有哪些目录三字词语有哪些三字词语有什么三个字比较好听的词语三个字的词语 三个字的词语有哪些三...
美国纽约是不是在纽约州,纽约在... 美国纽约是不是在纽约州目录美国纽约是不是在纽约州纽约在美国哪个州?是不是在纽约州纽约在哪个州美国纽约...
黑色的大蜜蜂是什么蜂,黑色个头... 黑色的大蜜蜂是什么蜂目录黑色的大蜜蜂是什么蜂黑色个头很大的蜜蜂是啥蜂黑色的体型较大毛茸茸的蜂. 是什...
水老鼠学名叫什么,水老鼠学名叫... 水老鼠学名叫什么目录水老鼠学名叫什么水老鼠学名叫什么?麝鼠学名是?水老鼠学名叫什么呢?水老鼠学名叫什...
五粮精酿和五粮液区别,五粮液和... 五粮精酿和五粮液区别目录五粮精酿和五粮液区别五粮液和五粮精酿的区别五粮液酿神是五粮液产的吗?与五粮液...
在家怎么炒鸡蛋才好吃又简单,怎... 在家怎么炒鸡蛋才好吃又简单目录在家怎么炒鸡蛋才好吃又简单怎么才能炒出好吃的炒鸡蛋?炒出滑嫩的鸡蛋怎么...
srs什么车 极速百科网 极速... srs什么车目录srs什么车srs什么车SR里什么车最好啊srs是什么车牌子srs什么车 带有...
吉祥有什么含义,吉祥是什么意思... 吉祥有什么含义目录吉祥有什么含义吉祥是什么意思?吉祥的意思是什么吉祥的意思怎样理解吉祥有什么含义 ...
赵丽颖老公是谁啊,赵丽颖有丈夫... 赵丽颖老公是谁啊目录赵丽颖老公是谁啊赵丽颖有丈夫了吗赵丽颖的老公是何炅吗赵丽颖怀孕老公是谁 当然是冯...
安全隐患排查内容有哪些,安全风... 安全隐患排查内容有哪些目录安全隐患排查内容有哪些安全风险隐患排查形式包括安全生产检查的类型和内容有哪...
蚊子包怎么快速消肿,蚊子叮咬怎... 蚊子包怎么快速消肿目录蚊子包怎么快速消肿蚊子叮咬怎么办?这些方法很管用蚊子叮咬后,别着急!试试这些小...
毒上三种发货的区别,毒上的普通... 毒上三种发货的区别目录毒上三种发货的区别毒上的普通发货,极速发货,闪电发货的区别是啥什么?普通发货有...