caoyang2002

C 语言结构体

· simons ·
编程

结构体内存对齐

#include <stdio.h>
struct S1{
    char c1;//1
    int i;//4
    char c2;//1
};

struct S2{
    char c1;//1
    char c2;//1
    int i;//4
};
int main(){
    printf("%d\n",sizeof(struct S1));//12
    printf("%d\n",sizeof(struct S2));//8
    return 0;
}

对齐规则:

  1. 第一个成员在与结构体变量偏移量为0的地址处

  2. 其它成员变量要对齐到某个数字(对齐数)的整数倍的地址处 对齐数 = 编译默认的一个对齐数与该成员大小的较小值。 vs中默认是8

    (即:第二个成员和编译器的默认对齐数比较大小,第三个成员和编译器的默认对齐数比较大小,第四个成员和编译器的默认对齐数比较大小…..)

  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍

  4. 如果嵌套了结构体的情况,嵌套结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体对齐数)的整数倍。

    如果编译器没有默认对齐数,那么大小就是成员的自身大小

    • S1

      struct S1{
          char c1;//1
          int i;//4
          char c2;//1
      };
    内存地址0123456789101112131415
    结构体成员c1\\\iiiic2\\\

    c1 为char类型,占用1个字节;

    i 为int类型,占用4个字节;

    #include <stdio.h>
    #include <stddef.h>
    
    struct S1{
        char c1;//1
        int i;//4
        char c2;//1
    };
    
    int main(){
        printf("%d\n",sizeof(struct S1));//12
    
        printf("%d\n",offsetof(struct S1,c1));//0  返回结构体成员在这个类型中的偏移量
        printf("%d\n",offsetof(struct S1,i));//4  返回结构体成员在这个类型中的偏移量
        printf("%d\n",offsetof(struct S1,c2));//8  返回结构体成员在这个类型中的偏移量
        return 0;
    }
    • S2

      struct S2{
          char c1;//1
          char c2;//1
          int i;//4
      };
      内存地址0123456789101112131415
      结构体成员c1c2\\iiii

      结构体总大小为最大对齐数(每个成员变量都一个对齐数)的整数倍:最后的i放入后已经是整数倍了,不用偏移

      #include <stdio.h>
      #include <stddef.h>
      
      struct S2{
          char c1;//1
          char c2;//1
          int i;//4
      };
      int main(){
      
          printf("%d\n",sizeof(struct S2));//8
          printf("%d\n",offsetof(struct S2,c1));//0  返回结构体成员在这个类型中的偏移量
          printf("%d\n",offsetof(struct S2,c2));//1  返回结构体成员在这个类型中的偏移量
          printf("%d\n",offsetof(struct S2,i));//4  返回结构体成员在这个类型中的偏移量
          return 0;
      }
  • 嵌套

    #include <stdio.h>
    #include <stddef.h>
    
    struct S3{
        double d;//8
        char c;//1
        int i;//4
    };
    
    struct S4{
        char c1;//1
        struct S3 s3;
        double d;
    };
    int main(){
        printf("%d\n",sizeof(struct S3));
        printf("%d\n",sizeof(struct S4));
        return 0;
    }
    • S3

      struct S3{
          double d;//8
          char c;//1
          int i;//4
      };
      内存地址01234567891011121314151617181920
      结构体成员ddddddddc\\\iiii

      结构体总大小为最大对齐数(每个成员变量都一个对齐数)的整数倍,当前是16,为最大8的整数倍。

      #include <stdio.h>
      
      struct S3{
          double d;//8
          char c;//1
          int i;//4
      };
      
      int main(){
          printf("%d\n",sizeof(struct S3));//16
          return 0;
      }

  • S4

    struct S4{
        char c1;//1
        struct S3 s3;
        double d;
    };
    内存地址01234567891011121314151617181920212223242526272829303132333435363738
    结构体成员c1\\\\\\\\s3s3s3s3s3s3s3s3s3s3s3s3s3s3s3s3dddddddd

    第一个成员:从0开始,占用1个字节

    第二个成员:嵌套结构体对齐到自己的最大对齐数的整数倍处,此处S3的最大对齐数是8,所以从8为位置处开始

    第三个成员:自己需要占用8个自己,如果当前在所占字节的整数倍处,则从当前开始,如果没有,则跳到整数倍位置处再开始

    结束位置是所有成员最大对齐数的整数倍,这里最大是8,32是8的整数倍,所以是32

    #include <stdio.h>
    
    struct S4{
        char c1;//1
        struct S3 s3;
        double d;
    };
    int main(){
        printf("%d\n",sizeof(struct S4));//32
        return 0;
    }

为什有内存对齐:

  1. 平台原因:不是所有的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能在某些地址处取得特定类型的数据,否则会抛出硬件异常。
  2. 性能原因:数据结构,尤其是栈,应尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要做两次内存访问;而对齐的内存访问仅需要一次访问。

结构体的内存对齐是拿空间换时间的做法

结构体在设计的时候,要尽量满足对齐:

让占用空间小的成员集中在一起

默认对齐数

在vs上默认对齐数是8

#include <stdio.h>
#pragma pack(8)//设置默认对齐数为8,一般是2的n次方(0,1,2,3,4......)
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)//还原默认对齐数为1

struct S{
    int i;//4个字节
    double d;//8个字节
};
int main(){
    printf("%d\n",sizeof(struct S));
    return 0;
}
  • 设置默认对齐数为8 —> 结构体占用16个字节

    内存地址01234567891011121314151617181920
    结构体成员iiii\\\\dddddddd
  • 设置默认对齐数为1 —> 结构体占用12个字节

    内存地址01234567891011121314151617181920
    结构体成员iiiidddddddd
    • 第一个成员:从0开始,占用4个字节,默认对齐数为4,对齐数:4;占用0-3,
    • 第二个成员:从4开始,占用字节数为8,默认对齐数是4,取较小的对齐数:4;占用4-11
    • 总大小是最大对齐数 :4,12是4的整数倍,所以是12