• C语言结构体的一些鲜为人知的小秘密


    目录

    一、结构体内存对齐规则:

    1.1范例

    1.2结构体内存对齐规则

    1.3自定义默认对齐数

    二、位段 

    2.1什么是位段

    2.2位段的内存分配

    2.3位段的不足

    三、枚举和联合体

    3.1枚举

    3.1.1枚举类型的定义

    3.1.2枚举类型的使用

    3.2联合体

    3.2.1联合体的定义

    3.2.2联合体的特点


    一、结构体内存对齐规则:

    1.1范例

    我们来看一下结构体所占用的内存大小:

    命名 A 和 B 的结构体成员都一样, 但是为什么他们占用的内存空间不一样呢?下面我们来介绍一下结构体内存的对齐规则。

    1.2结构体内存对齐规则

    首先我们要知道什么是对齐数,对齐数 = 编译器中默认的对齐数该成员类型变量的大小(单位是byte)中的较小值。其中,VS默认的对齐数是8,Linux无默认对齐数,对齐数是其本身。

    而在结构体中存放的数据,其地址并不是严格连续存放的,而是存放在其对齐数的整数倍

    编译器为结构体开创的大小,是最大对齐数的整数倍。

    1. struct stu
    2. {
    3. char name[20];
    4. int age;
    5. double grades;
    6. };
    7. int main()
    8. {
    9. printf("%d\n", sizeof(struct stu));
    10. //结果为32
    11. return 0;
    12. }

    1.3自定义默认对齐数

    之前在我们写三子棋时创建的 .h 文件中,我们就见过 #pragma 这个预处理指令,这里我们再次使用,可以改变我们的默认对齐数。

    1. #pragma pack(8)//设置默认对齐数为8
    2. struct A
    3. {
    4. int a;
    5. short b;
    6. int c;
    7. char d;
    8. };
    9. #pragma pack()//取消设置的默认对齐数,还原为默认
    10. #pragma pack(1)//设置默认对齐数为1
    11. struct B
    12. {
    13. int a;
    14. short b;
    15. char c;
    16. int d;
    17. };

    当我们把结构体 A 和 B 的默认对齐数都设置为1时,我们可以再次计算一下他们的大小

    可以看出,他们的内存竟然都变成了11,这也说明了我们设置的默认对齐数起作用了。 

    二、位段 

    2.1什么是位段

    很多朋友可能是第一次听说过位段,它是什么?为什么能和结构体扯上关系?

    接下来我们对比一下位段类型的结构体和正常的结构体:

    不难看出来,位段类型是在我们正常定义的变量基础上加上了 :数字 。其实这代表了我们定义的变量所需要占用的存储空间,单位是 bit

    我们分别来计算上面两种结构体的大小,我们可以得出,使用位段的结构体只需要8字节,不使用位段的结构体却要16个字节。所以,位段是用来节省空间的。

    2.2位段的内存分配

    我们来看个例子了解在VS中位段的内存分配。

    1. #include
    2. struct _Record_Struct
    3. {
    4. unsigned char Env_Alarm_ID : 4;
    5. unsigned char Para1 : 2;
    6. unsigned char state;
    7. unsigned char avail : 1;
    8. };
    9. int main()
    10. {
    11. sizeof(struct _Record_Struct);
    12. return 0;
    13. }

    我们在VS中运行这个程序也可以得到结果是3(byte),和我们图中的一样。

    位段虽然能节省空间,但当两个位段类型超过 1byte 时,还是会浪费第一个位段类型剩余的几个 bit ,开辟下一个字节空间来存放第二个位段类型 

    我们再来看个例子来更直观的感受每一个 bit 位是怎么分配的。

    1. struct S
    2. {
    3. char a : 3;
    4. char b : 4;
    5. char c : 5;
    6. char d : 4;
    7. };
    8. int main()
    9. {
    10. struct S s = { 0 };
    11. s.a = 10;
    12. s.b = 12;
    13. s.c = 3;
    14. s.d = 4;
    15. return 0;
    16. }

    接下来我们通过内存看一下他们存放的数据,每四个bit位在内存中对应一个十六进制数字:

    事实证明我们的理解是对的!

    2.3位段的不足

    我们上述只是讲解了VS中位段空间的分配,但是这仅仅是在VS中......

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

    三、枚举和联合体

    接下来我们介绍两种和结构体类似的自定义类型:枚举和联合体

    3.1枚举

    3.1.1枚举类型的定义

    我们先来看一下枚举的语法:

    1. enum Day//星期
    2. {
    3. Mon,
    4. Tues,
    5. Wed,
    6. Thur,
    7. Fri,
    8. Sat,
    9. Sun
    10. };

    enum + 命名,枚举中的类型用 , 隔开。

    这些可能取值都是有值的,默认从0开始,一次递增1,当然在定义的时候也可以赋初值。赋初值后定义的类型取值从初值开始递增1,如:

    1. enum Day
    2. {
    3. Mon,//0
    4. Tues = 2,//2
    5. Wed,//3
    6. Thur,//4
    7. //......
    8. Fri,
    9. Sat,
    10. Sun
    11. };

    3.1.2枚举类型的使用

    枚举类型能怎么使用呢? 其实它的使用在我们写一些小应用时用到的概率大一点:

    1. enum Day//星期
    2. {
    3. Mon = 1,
    4. Tues,
    5. Wed,
    6. Thur,
    7. Fri,
    8. Sat,
    9. Sun
    10. };
    11. int main()
    12. {
    13. int input = 0;
    14. scanf("%d", &input);
    15. switch (input)
    16. {
    17. case Mon:
    18. case Tues:
    19. case Wed:
    20. case Thur:
    21. case Fri:printf("今天是工作日\n"); break;
    22. case Sat:
    23. case Sun:printf("今天可以休息一下啦!\n"); break;
    24. }
    25. return 0;
    26. }

    当我们用枚举常量代替 case 情况的取值时,我们的代码是不是变得清晰易懂?

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

    3.2联合体

    3.2.1联合体的定义

    联合也是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。
    比如:

    1. //联合类型的声明
    2. union Un
    3. {
    4. char c;
    5. int i;
    6. };
    7. //联合变量的定义
    8. union Un un;
    9. //计算连个变量的大小
    10. printf("%d\n", sizeof(un));

     union + 命名,成员之间用 ; 相隔。

    3.2.2联合体的特点

    联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。

    联合体占用空间也遵循结构体对齐规则。

  • 相关阅读:
    4 | Nikto使用
    用友YonSuite“数智飞轮”用场景化告别产品与客户间的“翻译”
    【OHOS】常用命令整理
    java swing 布局心得(避免忘记)
    通过SVN拉取项目 步骤
    接口(接口相关含义,区别抽象类,接口回调)
    MySQL 8.0 新特性
    金融和大模型的“两层皮”问题
    如何使用ArcGIS Pro将等高线转DEM
    读《反无效努力工作法》
  • 原文地址:https://blog.csdn.net/m0_75186846/article/details/133278290