• 有关内存字节对齐问题的总结


        以前也折腾过这个问题,不过不如这次来的更深刻了。

        最早的资料来源是陈正冲·石虎的《C语言深度剖析》,但是看过之后还是有些模糊。xczhang的文章用了具体的例子来分析:

        http://www.cppblog.com/xczhang/archive/2007/12/23/39393.html

        他的原话是:

        “事实上,很多人对#pragma pack的理解是错误的。#pragma pack规定的对齐长度,实际使用的规则是:
        结构,联合,或者类的数据成员,第一个放在偏移为0的地方,以后每个数据成员的对齐,按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
        也就是说,当#pragma pack的值等于或超过所有数据成员长度的时候,这个值的大小将不产生任何效果。
        而结构整体的对齐,则按照结构体中最大的数据成员 和 #pragma pack指定值 之间,较小的那个进行。”

        这里提到了3个对比的优先级:#pragma pack(n)的参数n、结构体最大成员的长度、每个成员在“摆放”位置时自己的自身的长度。

        还是用一个例子来说明吧:

    struct TestStruct
    {
        char a;
        long long b;
        short c;
        //int d;
        //char d;
        short d;
         //用一个默认构造函数进行初始化,便于在“内存”面板查看内存块使用情况    TestStruct::TestStruct() : a('a'), b(10L), c(2), d(3){}
    };
    
    int normal_size = sizeof(TestStruct);
    printf("sizeof(TestStruct4) result is : %d\n", normal_size);

    成员a、b、c在内存中的排列情况是:

    61 cc  cc cc  cc  cc cc  cc   //成员a(char):a???????

    0a 00 00 00 00 00 00 00  //成员b(long long):10

    02 00                             //成员c(short):2

    解释:整个结构体最大成员为b,long long长度为8。首先放成员a:a为char型长度为1,放入[0]号位置。

             成员b长度为8,其首元素位置下标必须为8的倍数,取最小值,故将b从[8]开始摆放。

             成员c长度为2,首元素位置下标必须为2的倍数,此时最小下标为[16],放入。

    注意TestStruct最后一个成员d,当d的类型为char时,第三行排布情况为(假设d初始化为字符'd'):

    02 00 64 cc cc cc cc cc

    解释:d的自身长度为1,其下标必须为1的倍数,故将其放在c的后面(下标[18]),中间不留空位,剩余部分由“空位(cc)”填充。

    当d类型为short时,第三行排布情况为(假设d值为3):

    02 00 03 00 cc cc cc cc

    解释:d自身长度为2,满足2的倍数条件的最小下标为[18]

    当d类型为int时,第三行排布情况为(假设d值为3):

    02 00 cc cc 03 00 00 00

    解释:d自身长度为4,满足2的倍数条件的最小下标为[20]。故c、d之间补两个空位(cc)

    不加pragma pack(n),三种情况结构体大小都一样,为24。

    如果加了呢?这里以 pragma pack(2),即我们以2作为对齐长度

    解释:整个结构体最大成员为b,long long长度为8。首先放成员a:a为char型长度为1,放入[0]号位置。

    成员b长度为8,其首元素位置下标必须为8的倍数,取最小值,故将b从[8]开始摆放。

    成员c长度为2,首元素位置下标必须为2的倍数,此时最小下标为[16],放入。

    注意TestStruct最后一个成员d,当d的类型为char时,第三行排布情况为(假设d初始化为字符'd'):

    02 00 64 cc cc cc cc cc

    解释:d的自身长度为1,其下标必须为1的倍数,故将其放在c的后面(下标[18]),中间不留空位,剩余部分由“空位(cc)”填充。

    当d类型为short时,第三行排布情况为(假设d值为3):

    02 00 03 00 cc cc cc cc

    解释:d自身长度为2,满足2的倍数条件的最小下标为[18]

    当d类型为int时,第三行排布情况为(假设d值为3):

    02 00 cc cc 03 00 00 00

    解释:d自身长度为4,满足2的倍数条件的最小下标为[20]。故c、d之间补两个空位(cc)

    不加pragma pack(n),三种情况结构体大小都一样,为24。

    如果加了呢?这里以 pragma pack(2),即我们以2作为对齐长:

    #pragma pack(2)
    struct TestStruct
    {
        char a;
        long long b;
        short c;
        //int d;
        //char d;
        short d;
      //用一个默认构造函数进行初始化,便于在“内存”面板查看内存块使用情况
    TestStruct::TestStruct() : a('a'), b(10L), c(2), d(3){}
    }; #pragma pack() int normal_size = sizeof(TestStruct); printf("sizeof(TestStruct4) result is : %d\n", normal_size);

    成员a、b、c在内存中的排列情况是:

    61 cc     //成员a(char):a?

    0a 00

    00 00

    00 00

    00 00  //成员b(long long):10

    02 00  //成员c(short):2

    解释:整个结构体最大成员为b,long long长度为8。由于加了#pragma pack(2),2比8要小,整个结构体以2作为对齐长度。

            首先放成员a:a为char型长度为1,放入[0]号位置。

             成员b长度为8,其首元素位置下标必须为2的倍数,取最小值,故将b从[2]开始摆放。

             成员c长度为2,首元素位置下标必须为2的倍数,此时最小下标为[10],放入。

    注意TestStruct最后一个成员d,当d的类型为char时,第三行排布情况为(假设d初始化为字符'd'):

    02 00

    64 cc

    解释:d的自身长度为1,其下标必须为1的倍数,故将其放在c的后面(下标[12]),中间不留空位,最后一位由“空位(cc)”填充。

    此时结构体大小为14 = 2+8+2+2

    当d类型为short时,第三行排布情况为(假设d值为3):

    02 00

    03 00

    解释:d自身长度为2,满足2的倍数条件的最小下标为[12],此时结构体大小为14 = 2+8+2+2

    当d类型为int时,第三行排布情况为(假设d值为3):

    02 00

    03 00

    00 00

    解释:d自身长度为4,满足2的倍数条件的最小下标为[12]。int内存块视作拆成两个short

    此时结构体大小为:16 = 2+8+2+4

    #pragma pack(4)和#pragma pack(8)同理,这里不做赘述

  • 相关阅读:
    NSObject-拷贝 NSCopy-NSMutablecopy
    NSObject 排序
    iOS UIView上添加mp4视频
    OC语言中BOOL 和 bool 区别
    便利初始化浅析
    可变字符与不可不可变字符串的心得
    博客开通随笔
    OC总结第五讲:多态
    oc总结第四讲:属性
    oc总结第三讲 setter getter 方法 点运算符
  • 原文地址:https://www.cnblogs.com/sunrisezhang/p/3036332.html
Copyright © 2020-2023  润新知