C语言 结构体(联合体)对齐规则

时间:2022-09-05 18:38:11
/* 结构体(联合体)对齐规则 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
    * 原则1、第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。

    * 原则2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最宽基本类型大小的整数倍地址开始存储。

    *原则3、结构体的总大小为结构体最宽基本类型成员(成员可以是基本类型,也可以是结构体)大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节

    *最宽基本类型的概念,所谓基本类型是指像char、short、int、float、double这样的内置数据类型。

*/

struct Data1
{
    int c;   
    char a;  
    char b;  
};

/*
struct Data1分析
分析对齐数
对齐数的值是 结构体中最宽基本类型成员
struct Data1最宽类型是int,占4个字节大小,所以对齐数的值是4

根据原则1(第一个数据成员放在offset为0的地方),成员属性c从offset为0的位置开始,成员属性c大小是4个字节,成员属性c从offset为0的位置开始存储,占据4个字节大小

成员属性a从offset为4的位置开始,根据原则1(以后每个数据成员存储的起始位置要从该成员大小的整数倍开始) ,现在offset是4,是1的整数倍
所以成员属性a从offset为4的位置开始存储,占据1个字节大小

成员属性b从offset为5的位置开始,根据原则1(以后每个数据成员存储的起始位置要从该成员大小的整数倍开始) ,现在offset是5,是1的整数倍
所以成员属性b从offset为5的位置开始存储,占据1个字节大小

现在struct Data1一共占据6个字节大小的空间,根据原则3(必须是其内部最大成员的整数倍),struct Data1最宽基本类型是int,占4个字节大小
因此结构体的总大小必须是4的倍数,6不是4的倍数,补齐2个字节,变成8个字节
结论:struct Data1 大小是8个字节

*/

struct Data2
{
    char a;  
    int c;   
    char b;  
};

/*
struct Data2分析
分析对齐数
struct Data2最宽类型是int,占4个字节大小,所以对齐数的值是4

根据原则1(第一个数据成员放在offset为0的地方),成员属性a从offset为0的位置开始,成员属性a大小是1个字节,成员属性a从offset为0的位置开始存储,占据1个字节大小

成员属性c是int类型,根据原则1(以后每个数据成员存储的起始位置要从该成员大小的整数倍开始),现在offset是1,1不是4的整数倍
编译器填充3个字节,现在offset为4 , 4是int的整数倍,成员属性c从offset为4的位置开始存储,占据4个字节大小

成员属性b从offset为8的位置开始,根据原则1(以后每个数据成员存储的起始位置要从该成员大小的整数倍开始) ,现在offset是8,是1的整数倍
所以成员属性b从offset为8的位置开始存储,占据1个字节大小

现在struct Data2一共占据9个字节大小的空间,根据原则3(必须是其内部最大成员的整数倍),struct Data2最宽基本类型是int,占4个字节大小
因此结构体的总大小必须是4的倍数,9不是4的倍数,补齐3个字节,变成12个字节
结论:struct Data1 大小是12个字节

*/

struct Data3
{
    char a;
    short b;
    double c;
};

/*
struct Data3分析
分析对齐数
struct Data3最宽类型是double,占8个字节大小,所以对齐数的值是8

根据原则1(第一个数据成员放在offset为0的地方),成员属性a从offset为0的位置开始,成员属性a大小是1个字节,成员属性a从offset为0的位置开始存储,占据1个字节大小

成员属性b从offset为1的位置开始,根据原则1(以后每个数据成员存储的起始位置要从该成员大小的整数倍开始) ,现在offset是1,不是short的整数倍
编译器填充1个字节,现在offset为2 , 2是short的整数倍,成员属性b从offset为2的位置开始存储,占据2个字节大小

成员属性c从offset为4的位置开始,根据原则1(以后每个数据成员存储的起始位置要从该成员大小的整数倍开始) ,现在offset是4,不是double的整数倍
编译器填充4个字节,现在offset为8 , 8是double的整数倍,成员属性c从offset为8的位置开始存储,占据8个字节大小

现在struct Data3一共占据16个字节大小的空间,根据原则3(必须是其内部最大成员的整数倍),struct Data3最宽基本类型是double,占8个字节大小
因此结构体的总大小必须是8的倍数,16是8的倍数,不用填充字节
结论:struct Data3 大小是16个字节

*/

struct Data4
{
    short a;
    char b;
    double c;
    char d[5];
};

/*
struct Data4分析
分析对齐数
struct Data3最宽类型是double,占8个字节大小,所以对齐数的值是8
char d[5]并非基本类型

根据原则1(第一个数据成员放在offset为0的地方),成员属性a从offset为0的位置开始,成员属性a大小是2个字节,成员属性a从offset为0的位置开始存储,占据2个字节大小

成员属性b从offset为2的位置开始,根据原则1(以后每个数据成员存储的起始位置要从该成员大小的整数倍开始) ,现在offset是2,是1的整数倍
成员属性b从offset为2的位置开始存储,占据1个字节大小

成员属性c从offset为3的位置开始,根据原则1(以后每个数据成员存储的起始位置要从该成员大小的整数倍开始) ,现在offset是3,不是double的整数倍
编译器填充5个字节,现在offset为8 , 8是double的整数倍,成员属性c从offset为8的位置开始存储,占据8个字节大小

成员属性d从offset为16的位置开始,根据原则1(以后每个数据成员存储的起始位置要从该成员大小的整数倍开始) ,现在offset是16,是char的整数倍
成员属性d从offset为16的位置开始存储,占据1个字节大小

现在struct Data4一共占据17个字节大小的空间,根据原则3(必须是其内部最大成员的整数倍),struct Data3最宽基本类型是double,占8个字节大小
因此结构体的总大小必须是8的倍数,17不是8的倍数,需要填充7个字节
结论:struct Data4 大小是24个字节

*/

struct Data5
{
    short a;
    struct Data4 b;
};

/*
struct Data5分析
分析对齐数
根据原则2, struct Data4最宽类型是double 占8个字节大小,而struct Data5成员a占2个字节,所以对齐数的值是8

根据原则1(第一个数据成员放在offset为0的地方),成员属性a从offset为0的位置开始,成员属性a大小是2个字节,成员属性a从offset为0的位置开始存储,占据2个字节大小

成员属性b从offset为2的位置开始,根据原则2(结构体成员要从其内部最大元素大小的整数倍地址开始存储) ,现在offset是2,不是double的整数倍
编译器填充6个字节,现在offset为8 , 8是double的整数倍,成员属性b从offset为8的位置开始存储,占据24个字节大小

现在struct Data5一共占据32个字节大小的空间,根据原则3(必须是其内部最大成员的整数倍),struct Data5最宽基本类型是double,占8个字节大小
因此结构体的总大小必须是8的倍数,32是8的倍数,不需要填充字节
结论:struct Data5 大小是32个字节

*/

struct Data6
{
    double a;
    struct Data1 b;
};

/*
struct Data6分析
分析对齐数
根据原则2, struct Data1最宽类型是int 占4个字节大小,而struct Data5成员a 占8个字节,所以对齐数的值是8

根据原则1(第一个数据成员放在offset为0的地方),成员属性a从offset为0的位置开始,成员属性a大小是8个字节,成员属性a从offset为0的位置开始存储,占据8个字节大小

成员属性b从offset为8的位置开始,根据原则2(结构体成员要从其内部最大元素大小的整数倍地址开始存储) ,现在offset是8 ,是 int 的整数倍
成员属性b从offset为8的位置开始存储,占据8个字节大小

现在struct Data6一共占据16个字节大小的空间,根据原则3(必须是其内部最大成员的整数倍),struct Data6最宽基本类型是double,占8个字节大小
因此结构体的总大小必须是8的倍数,16是8的倍数,不需要填充字节
结论:struct Data6 大小是16个字节

*/

void test()
{
    printf("----struct size---1-[%d]------\n", sizeof(struct Data1));   //8
    printf("----struct size--2--[%d]------\n", sizeof(struct Data2));   //12
    printf("----struct size--3--[%d]------\n", sizeof(struct Data3));   //16
    printf("----struct size--4--[%d]------\n", sizeof(struct Data4));   //24
    printf("----struct size--5--[%d]------\n", sizeof(struct Data5));   //32
    printf("----struct size--6--[%d]------\n", sizeof(struct Data6));   //16
}

int main()
{
    test();
    printf("-----ok------\n");
    getchar();
    return 0;
}