• c++中的对齐问题


    c++中的对齐问题

    需要对齐的原因

    尽管内存是以字节为单位,但是大部分处理器并不是按字节块来存取内存的.它一般会以双字节,四字节,8字节,16字节甚至32字节为单位来存取内存,我们将上述这些存取单位称为内存存取粒度.

    现在考虑4字节存取粒度的处理器取int类型变量(32位系统),该处理器只能从地址为4的倍数的内存开始读取数据

    假如没有内存对齐机制,数据可以任意存放,现在一个int变量存放在从地址1开始的联系四个字节地址中,该处理器去取数据时,要先从0地址开始读取第一个4字节块,剔除不想要的字节(0地址),然后从地址4开始读取下一个4字节块,同样剔除不要的数据(5,6,7地址),最后留下的两块数据合并放入寄存器.这需要做很多工作.

    bg1

    对齐的规则

    有效对齐值:是 #pragma pack(n)和结构体中最长数据类型长度中较小的那个。有效对齐值也叫对齐单位

    注意:
    #pragma pack(n)中的n可以取(1 , 2 , 4 , 8 , 16)中的任意一值。

    2)规则:

    • 结构体变量的首地址是有效对齐值(对齐单位)的整数倍。

    • 结构体第一个成员的偏移量(offset)为0,以后每个成员相对于结构体首地址的 offset 都是该成员大小与有效对齐值中较小那个的整数倍,如有需要编译器会在成员之间加上填充字节。

    • 结构体的总大小为有效对齐值的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。

    • 结构体内类型相同的连续元素将在连续的空间内,和数组一样。

    运用上面的规则,下面通过实际的例子进行计算。

    例1:

    #include 
    
    struct MyStruct {
        char c;
        int i;
        short s;
    };
    
    int main()
    {
        MyStruct obj;
        std::cout << "start addr of obj = " << (void*)&obj << std::endl;
        std::cout << "offset of c = "  << offsetof(MyStruct,c) << std::endl;
        std::cout << "offset of i = " << offsetof(MyStruct,i) << std::endl;
        std::cout << "offset of s = " << offsetof(MyStruct,s) << std::endl;
        std::cout << "sizeof MyStruct = " << sizeof(MyStruct);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    执行结果如下:

    start of obj = 0x7fff2e8d1e94
    offset of c = 0
    offset of i = 4
    offset of s = 8
    sizeof MyStruct = 12
    
    • 1
    • 2
    • 3
    • 4
    • 5

    结构中最长的数据类型是int,长度也为4。因此结构体的有效对齐值是4。

    对于c变量而言,没有悬念,将排在0偏移地址处。

    对于变量i,类型为int,长度为4,int和有效对齐值的最小值为4,因此i需要排布在4的整数倍上,因此第一个符合要求的偏移量就是4。

    对于变量s,类型为short,长度为2,short和有效对齐值二者中的最小值为2,第一个符合要求的地址为8。

    到目前为止,使用的空间大小是10,而结构体大小需要满足有效对齐值的整数倍,因此需要2个填充,因此结构体最终大小是12。

    MyStruct分布

    例2:

    #include 
    #pragma pack(2)
    struct MyStruct {
        char c;
        int i;
        short s;
    };
    
    int main()
    {
        MyStruct obj;
        std::cout << "start addr of obj = " << (void*)&obj << std::endl;
        std::cout << "offset of c = "  << offsetof(MyStruct,c) << std::endl;
        std::cout << "offset of i = " << offsetof(MyStruct,i) << std::endl;
        std::cout << "offset of s = " << offsetof(MyStruct,s) << std::endl;
        std::cout << "sizeof MyStruct = " << sizeof(MyStruct);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    执行结果如下:

    start addr of obj = 0x7fff488e3418
    offset of c = 0
    offset of i = 2
    offset of s = 6
    sizeof MyStruct = 8
    
    • 1
    • 2
    • 3
    • 4
    • 5

    首先#pragma pack设置的对齐值是2,结构中最长的数据类型是int,长度也为4。因此结构体的有效对齐值是2。

    对于c变量而言,没有悬念,将排在0偏移地址处。

    对于变量i,类型为int,长度为4,int和有效对齐值的最小值为2,因此i需要排布在2的整数倍上,因此第一个符合要求的偏移量就是2。

    对于变量s,类型为short,长度为2,short和有效对齐值二者中的最小值为2,第一个符合要求的地址为6。

    到目前为止,使用的空间大小是8,已经满足结构体大小是有效对齐值的整数倍的要求。

    MyStruct分布2

    #include 
    #pragma pack(1)
    struct MyStruct {
        char c;
        int i;
        short s;
    };
    
    int main()
    {
        MyStruct obj;
        std::cout << "start addr of obj = " << (void*)&obj << std::endl;
        std::cout << "offset of c = "  << offsetof(MyStruct,c) << std::endl;
        std::cout << "offset of i = " << offsetof(MyStruct,i) << std::endl;
        std::cout << "offset of s = " << offsetof(MyStruct,s) << std::endl;
        std::cout << "sizeof MyStruct = " << sizeof(MyStruct);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    执行结果如下:

    start addr of obj = 0x7ffe96c067a9
    offset of c = 0
    offset of i = 1
    offset of s = 5
    sizeof MyStruct = 7
    
    • 1
    • 2
    • 3
    • 4
    • 5

    首先#pragma pack设置的对齐值是1,结构中最长的数据类型是int,长度也为4。因此结构体的有效对齐值是1。

    对于c变量而言,没有悬念,将排在0偏移地址处。

    对于变量i,类型为int,长度为4,int和有效对齐值的最小值为,因此i需要排布在2的整数倍上,因此第一个符合要求的偏移量就是1。

    对于变量s,类型为short,长度为2,short和有效对齐值二者中的最小值为2,第一个符合要求的地址为5。

    到目前为止,使用的空间大小是7,已经满足结构体大小是有效对齐值的整数倍的要求。

    MyStruct分布3

    例4:

    #include 
    #include 
    
    struct MyStruct {
        char c;
        __m128i i;
    };
    
    int main()
    {
        MyStruct obj;
        std::cout << "start addr of obj = " << (void*)&obj << std::endl;
        std::cout << "offset of c = "  << offsetof(MyStruct,c) << std::endl;
        std::cout << "offset of i = " << offsetof(MyStruct,i) << std::endl;
        std::cout << "sizeof MyStruct = " << sizeof(MyStruct);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    执行结果如下:

    start addr of obj = 0x7fff9d47cd90
    offset of c = 0
    offset of i = 16
    sizeof MyStruct = 32
    
    • 1
    • 2
    • 3
    • 4

    首先,结构中最长的数据类型是__m128i,长度为16。因此结构体的有效对齐值是16。

    对于c变量而言,没有悬念,将排在0偏移地址处。

    对于变量i,类型为__m128i,长度为16,__m128i和有效对齐值的最小值为16,因此i需要排布在2的整数倍上,因此第一个符合要求的偏移量就是16。

    MyStruct分布4

    例5:

    #include 
    #include 
    
    #pragma pack(8)
    struct MyStruct {
        char c;
        __m128i i;
    };
    
    int main()
    {
        MyStruct obj;
        std::cout << "start addr of obj = " << (void*)&obj << std::endl;
        std::cout << "offset of c = "  << offsetof(MyStruct,c) << std::endl;
        std::cout << "offset of i = " << offsetof(MyStruct,i) << std::endl;
        std::cout << "sizeof MyStruct = " << sizeof(MyStruct);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    执行结果如下:

    start addr of obj = 0x7ffddbec2c40
    offset of c = 0
    offset of i = 8
    sizeof MyStruct = 24
    
    • 1
    • 2
    • 3
    • 4

    首先#pragma pack设置的对齐值是8,结构中最长的数据类型是__m128i,长度为16。因此结构体的有效对齐值是8。

    对于c变量而言,没有悬念,将排在0偏移地址处。

    对于变量i,类型为__m128i,长度为16,__m128i和有效对齐值的最小值为8,因此i需要排布在2的整数倍上,因此第一个符合要求的偏移量就是8。

    MyStruct分布5

    总结

    • 为了高效的访问内存数据,通常需要对内存数据进行对齐。
    • #pragma pack(n)用于设置的对齐有效值,如果设置比结构体的最长成员还大的对齐值将是无效的。
  • 相关阅读:
    C++编程案例讲解-基于结构体的控制台通讯录管理系统
    七.音视频编辑-创建视频过渡-应用
    安卓使用android studio跨进程通信之AIDL
    上海老牌IB名校:世外系学校大盘点
    【跟小嘉学 Rust 编程】二十二、常用 API
    Verilog中关于reg [3:0] a [7:0] 和 reg [3:0] [7:0] a的区别
    leetcode 606.根据二叉树创建字符串
    Web安全知识
    #循循渐进学51单片机#步进电机与蜂鸣器#not.8
    为什么说继承是把双刃剑
  • 原文地址:https://blog.csdn.net/qq_31442743/article/details/132695982