• 结构体,联合体与位段


    1.结构体的内存对齐(计算结构体的大小)

    1.1 为什么需要结构体内存对齐?

    原因1:平台原因 

    不是所有的硬件平台都能访问任意地址上的任意数据的;某些平台只能在某些地址处取得某些特定类型的数据,否则抛出硬件异常。
    比如,当一个平台要取一个整型数据时只能在地址为4的倍数的位置取得,那么这时就需要内存对齐,否则无法访问到该整型数据。

    原因2: 性能原因

    数据结构(尤其是栈)应该尽可能的在自然边界上对齐。原因在于,为了访问未对齐内存,处理器需要作两次内存访问;而对齐的内存访问仅需一次。比如说下面这个整形i,我们想在一个32位机器访问它,一次读取四个字节,就需要两次才能访问完整的i,如果对齐了就只需要一次访问即可.

    1.2 结构体内存对齐规则 

    1. struct S {
    2. char c;
    3. int i;
    4. double d;
    5. };

    1.算出每个成员的对齐数(VS的默认对齐数是8,对齐数就是本身成员变量)

    2.将每个元素对齐到较小值的整数倍下面.

    3.通过最大对齐数知道大小最后的大小,比如说这个结构体的大小一定要是最大对齐数的整数倍,最大对齐数就是上面求出的较小值中的最大值.(是8),此时大小是16,正好是8的倍数,大多数情况下,此时的大小并不是最大对齐数的整数倍,这个时候我们只需补齐即可. 

    1.3 设计结构体的技巧 

    其实在我们设计结构体的时候,如果结构体成员的顺序设计得合理的话,是可以避免不必要的内存消耗的。
    两个结构体的成员变量相同,但是成员变量的顺序不同,可能就会出现结构体的大小不同的情况: 

    1. struct S1
    2. {
    3. char a;
    4. char b;
    5. int c;
    6. };
    7. struct S2
    8. {
    9. char a;
    10. int c;
    11. char b;
    12. };

    这个时候我们发现,结构体S1占用的是8个字节,而结构体S2就占用了12个字节,所以我们在设计结构体的时候需要将占用内存小的元素放在一块儿,可以避免内存的浪费.

    1.4 修改默认对齐数 

    在VS2022中,我们可以自由的修改默认对齐数来满足我们的内存需求.

    #pragma pack()
    

    只需在pack中填入你设置的默认对齐数即可.

    2.联合体 

    2.1 什么是联合体

    在C语言中,存在这样一个类型,可以让多个变量共用一块内存,这就是联合体.

    使用场景:在某个变量不使用的时候可以使用其他变量.

    缺点:在使用其他变量的时候会改变内存中的数据

    1. union 联合名
    2. {
    3. 成员表
    4. };

    2.2 联合体的创建方式(和结构体相似)

    1.先创建模板,再定义变量

    1. // 创建联合体模板union u
    2. union u
    3. {
    4. int i;
    5. char c;
    6. };
    7. // 使用该联合体模板创建两个变量a, b
    8. union u a,b;

    2.创建模板时同时定义变量

    1. // 创建联合体模板union u的同时定义两个变量a、b
    2. union u
    3. {
    4. int i;
    5. char c;
    6. };

    3.匿名联合体

    1. union
    2. {
    3. int i;
    4. char c;
    5. }a,b;

    4.typedef

    1. typedef union u
    2. {
    3. int i;
    4. char c;
    5. }U;
    6. U a = {1,'a'};

    2.3 初始化

    1. U a;
    2. a.i = 10;
    3. U b = a; /* 1、把一个联合初始化为另一个同类型的联合; */
    4. U c = {20}; /* 2、初始化联合的第一个成员; */
    5. U d = {.i = 30}; /* 3、根据C99标准,使用指定初始化器。 */

    2.4 应用:判断机器的大小端

    1. typedef union u
    2. {
    3. int i;
    4. char c;
    5. }U;
    6. U u;
    7. u.i = 0x12345678
    8. //打印c,如果是78就是小端,如果是12就是大端

    3.位段

    3.1 什么是位段

    位段是通过结构体来实现的一种以位(bit位)为单位的数据存储结构,它可以把数据以位的形式紧凑的储存,并允许程序员对此结构的位进行操作.这样可以节省空间.

    注:位段里面的成员只能有整形家族来组成,否则会报错.位段后面分配的比特位不能超过原本类型的大小.

    1. struct A
    2. {
    3. int a:2;
    4. int b:5;
    5. int c:10;
    6. int d:30;
    7. };

    3.2 位段内存计算

    我们以这个结构体举例

    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. printf("%d\n", sizeof(s));
    12. return 0;
    13. }

    实际上结果是3个字节,我们画图来看一下,这样相对直接使用结构体来说还是节省了不少空间的.

    3.3 位段的跨平台问题

    1.int型位段成员会被当成有符号数还是无符号数是不确定的
    2.位段中最大位数目是不确定的(在16位机器上int型最大为16,而在32为机器上int型最大为3.32,如若写成27,那么16位机器就会出问题)
    4.位段的成员在内存中到底是从左向右分配,还是从右向左分配尚未定义
    5.当一个结构体包含两个位段,第二个位段比较大,无法容纳于第一个位段剩余的位时, 是舍弃剩余的位还是利用,是不确定的。
    综上所述:位段是不跨平台的

  • 相关阅读:
    从Matlab实例学习蚁群算法(2)
    【iOS】—— autoreleasePool以及总结
    FANUC机器人_通过ROBOGUIDE从零开始做一个离线仿真项目(3)
    rabbitMQ
    1) 数字信号及其基本运算
    备份程序 bacula和bacula-api 安装
    树叶识别系统python+Django网页界面+TensorFlow+算法模型+数据集+图像识别分类
    Java多线程悲观锁和乐观锁
    Postgresql关于EOH的使用注意
    vscode远程连接及调试
  • 原文地址:https://blog.csdn.net/qiuqiushuibx/article/details/133221050