美文网首页
C 语言学习(12) ---- C语言结构体对齐

C 语言学习(12) ---- C语言结构体对齐

作者: 特立独行的佩奇 | 来源:发表于2023-05-08 20:35 被阅读0次

字节对齐

现在计算机系统的内存都是按照字节划分的,理论上对任何变量的访问可以从任何地址开始,但是实际情况是访问特定的变量经常在特定的内存地址访问,这就需要各种类型的数据按照一定的规则在空间上进行排列,而不是顺序排放,这就是对齐的概念;
比如在32bit system 下,一个4字节的int,如果它的地址是0x00000004 (4 的倍数),那么它就是对齐的;如果是0x00000002(非4 的倍数),那么它就是非对齐的

  • 为什么要使用字节对齐?
    字节对齐的根本原因是在于CPU访问效率的问题,上面的例子中如果地址时0x00000002,那么CPU 需要访问两次内存,
    第一次访问0x000000002 ~ 0x00000003 得到一个2 byte 的内容;
    第二次访问0x000000004 ~ 0x00000005 在得到一个2 byte的内容,组合得到整形数据
    如果变量在对齐位置上,就可以一次取出数据,一些系统对对齐比较严格,比如spar 系统,取未对齐的数据就会发生错误;在x86 上不会产生错误,只是效率下降;不同的对齐方式,在内存中存储的方法可能不一样,合理利用字节对齐,可以有效节约存储空间

结构体对齐

结构体对齐的规则首先要看有没有用#pragma pack宏声明,使用这个预编译指令可以改变对齐规则,
有宏定义的情况下结构体自身的大小应为预编译指定规定对齐的大小的整数倍,同时结构体内的成员的地址也会按照预编译指定规定的大小对齐,#pragma pack 参数可以是 "1" "2" "4" "8" 或者 "16"

未使用pragma pack宏声明

未使用pragma pack宏声明的情况下,遵循下面三个原则:

  1. 第一个成员的首地址假设为0
  2. 每个成员的首地址大小是其自身大小的整数倍
  3. 结构体的总大小,是其成员中所包含的最大类型size大小的整数倍

示例一:

//  struct = 1+2+4+8
struct demostruct {
                 //  baseAddr     length  padding
    uint8_t a;  //  addr         1        1
    uint16_t b; //  addr+2       2        0
    uint32_t c; //  addr+4       4        0
    uint64_t d; //  addr+8       8        0
};
  • 上面的例子中如果没有结构体的内存对齐,真实的大小为 1 + 2 + 4+ 8 = 15 字节
    按照结构体里成员地址需要是其自身大小的整数倍的原则 假定为结构体首地址为 addr
  1. 第一个成员 a 字节首地址 addr,长度为 1
  2. 第二个成员 b 字节首地址理论上为 addr + 1,但是按照地址对齐原则,地址必须是 2 的整数倍,所以在 a 后补充一个字节,b的首地址变为 add + 2,长度为 2
  3. 第三个成员 c 首地址理论上为 addr + 4,符合是自身长度整数倍的原则,长度为 4
  4. 第四个成员 d 首地址 addr + 8,符合是自身长度整数倍的原则
  5. 结构体大小为 16字节,结构体内包含最大成员占用 8 字节,符合上述原则

打印出的结构体大小和各成员内存地址如下:
struct_a size 16 a addr:008726E8 b:008726EA c addr:008726EC d addr:008726F0

对齐方法01.jpg

示例二:

//  struct = 1+8+1+1
struct demostruct_b {
    //  baseAddr     length  padding
    //  addr         1        7
    //  addr+8       8        0
    //  addr+16      1        0
    //  addr+17      1        0
    uint8_t a; 
    uint64_t b;
    uint8_t c;
    uint8_t d;
};
  • 上面的例子中如果没有结构体的内存对齐,真实的大小为 1 + 1 + 8+ 1 = 11 字节
  1. 第一个成员 a 字节首地址 addr,长度为 1
  2. 第二个成员 b 字节首地址理论上为 addr + 1,但是按照地址对齐原则,地址必须是 8 的整数倍,所以在 a 后补充7个字节,首地址变为 add + 8 长度为 8,
  3. 第三个成员 c 首地址为 addr + 16,长度为 1
  4. 第四个成员 d 首地址 addr + 17,长度为 1
  5. 结构体大小为 18 字节,结构体内包含最大成员占用 8 字节,所以还需要在最后补充 6 字节,共 24 字节

打印出的结构体大小和各成员内存地址如下:
struct_b size 24 a addr:008726F8 b:00872700 c addr:00872708 addr:00872709

对齐方法02.jpg

使用#pragma pack宏声明

#pragma pack(push)
#pragma pack(4)
//  struct = 1+8+1+1
struct demostruct_c {
    //  baseAddr     length  padding
    //  addr         1        3
    //  addr+4       8        0
    //  addr+12      1        0
    //  addr+13      1        0
    uint8_t a;
    uint64_t b;
    uint8_t c;
    uint8_t d;
};
#pragma pack(pop)
  • 上面的例子中用宏定义 按照 4 字节的方式对齐
  1. 第一个成员 a 字节首地址 addr,长度为 1
  2. 第二个成员 b 字节首地址理论上为 addr + 1,但是按照地址对齐原则,地址必须是 4 的整数倍,所以在 a 后补充3个字节,首地址变为 add + 4 长度为 8,
  3. 第三个成员 c 首地址为 addr + 12,长度为 1
  4. 第四个成员 d 首地址 addr + 13,长度为 1
  5. 结构体大小为14 字节,不是 pack(4) 的整数倍,所以在最后补充 2 字节,共 16 字节

struct_c size 16 a addr:00872710 b:00872714 c addr:0087271C addr:0087271D

pack对齐方法01.jpg

注意:
当#pragma pack 设定的 pack 值大于结构体中最大成员的size 值时,应当以结构体中最大成员的size作为 pack 的值
换句话说,应该以这两者中较小的值作为 pack 的值

#pragma pack(push)
#pragma pack(8)
//  struct = 1+4 +2
struct demostruct_d {
    //  baseAddr     length  padding
    //  addr         1        3
    //  addr+4       4        0
    //  addr+8       2        0
    uint8_t a;
    uint32_t c;
    uint16_t b;
};
#pragma pack(pop)

验证结果是12 字节,不是以设定的 pack 值 8
*struct_d size 12 a addr:00EF2720 b:00EF2728 c addr:00EF2724

相关文章

  • 解析C语言结构体对齐(内存对齐问题)

    解析C语言结构体对齐(内存对齐问题) C语言结构体对齐也是老生常谈的话题了。基本上是面试题的必考题。内容虽然很基础...

  • c语言结构体对齐

    1.什么是字节对齐 结构体里面一般会按照某种规则去进行字节对齐默认规则如下:对齐是按照结构体中长度最长的变量来对齐...

  • C语言结构体对齐

    对齐原则 原则A:struct或者union的成员,第一个成员在偏移0的位置,之后的每个成员的起始位置必须是当前成...

  • 结构体内存对齐

    前言 OC语言底层是基于c和c++的,而NSObject在底层也是用结构体实现的,所以了解了结构体的内存对齐问题对...

  • C语言结构体用法很多,坑也很多

    C语言可谓是编程界的传奇语言,历经几 十 年,依然排名前列。 本文主要说的是C语言中的结构体,结构体是C语言中重要...

  • C++语言学习之面向对象

    1.C语言与C++语言的区别 C++面向对象 C 面向过程 函数+结构体 C++可以运行调用C语言 反之 C语言无...

  • C语言和OC的结构体(struct)

    Struct(结构体) 1.结构体定义 2.结构体变量 3.结构体数组 4.C语言结构体指针 5.C语言共用体 6...

  • C语言结构体

    结构体 本文介绍C语言结构体,struct 在C++中功能相对C较多,相当于类,这里暂时不讨论,本文单独讨论C语言...

  • C语言学习笔记-结构体占用内存大小的计算

    引言 结构体在C语言中虽然经常使用,但是怎么计算一个结构体占用多大的内存,很多C语言的新手都没注意过,其实C语言的...

  • C语言结构体学习

    一、结构体的初始化 1、通过类型初始化 比如: 2、在定义结构的同时定义变量 3、直接定义结构体 在这里只是定义了...

网友评论

      本文标题:C 语言学习(12) ---- C语言结构体对齐

      本文链接:https://www.haomeiwen.com/subject/wnplsdtx.html