结构体对齐

观察到一个现象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct S1 {
char a;
int b;
char c;
};
struct S2 { //顺序不一样大小也不一样
int a;
char b;
char c;
};
int main(int argc, char* argv[]) {
S1 x;
S2 y;

printf("%d\n", sizeof(x)); //12
printf("%d\n", sizeof(y)); //8

printf("%d\n", sizeof(S1)); //12
printf("%d\n", sizeof(S2)); //8

return 0;
}
  • 为什么要有数据对齐?
    本质:效率还是空间,二选一的结果

  • 选择空间还是效率?
    #pragma pack 的基本用法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #pragma pack(n)
    //结构体。。。
    #pragma pack()

    #pragma pack(4)
    struct test {
    int a;
    __int64 b;
    char c;
    }
    #pragma pack()

    对齐参数:n 为字节对齐数,其取值为 1、2、4、8,默认为 8。整个结构体的占用内存大小也应当是 n 的整数倍。
    如果这个值比结构体成员的 sizeof 值小,那么该成员的偏移量应该以此值为准。也就是说,结构体成员的偏移量需要取二者之间的最小值
    各种对齐参数下数据的存储形式:

    1字节对齐是最省空间的,看一下他在内存中是怎么存的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #pragma pack(1)
    struct test {
    int a;
    __int64 b;
    char c;
    }
    #pragma pack()

    int main(int argc, char* argv[]) {
    test t;
    t.a = 1;
    t.b = 2;
    t.c = 3;
    printf("%d\n", sizeof(test)); //13
    return 0;
    }

  • 对齐原则
    原则一:数据成员对齐规则——结构的数据成员,第一个数据成员放在 offset 为 0 的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如 int 在 32 位机为 4 字节,则要从 4 的整数倍地址开始存储)。
    原则二:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
    原则三:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储(struct a 里存有 struct b,b 里有 char、int、double 等元素,那 b 应该从 8 的整数倍开始存储)。
    原则四:对齐参数如果比结构体成员的 sizeof 值小,该成员的偏移量应该以此值为准。也就是说,结构体成员的偏移量应该取二者的最小值。每个成员的 offset 满足自己的对齐要求即可
    以上四个原则都是在默认对齐参数是 8,并且成员都是 char, short, int, float, double, __int64 这种的情况下的

  • 三个案例(对齐参数为默认值8):

    1
    2
    3
    4
    struct Test {
    char a;
    int b;
    };

    1
    2
    3
    4
    5
    struct Test {
    int a;
    __int64 b;
    char c;
    };

    1
    2
    3
    4
    5
    6
    struct Test {
    int a;
    __int64 b;
    char c;
    char d;
    };

  • 建议:定义的时候按照数据类型从小到大的顺序,这样可以尽可能避免空间浪费

typedef 和结构体数组就不记了,都是基础正向的

课后作业

正向代码部分略

  1. 分析下面结构体的内存分配