计算结构体的大小

本文详细介绍了结构体在内存中的布局规则,包括地址对齐原则、成员包含数组和结构体的情况,以及指定对齐值的影响。通过实例代码展示了结构体大小的计算过程,揭示了编译器如何处理结构体内部的字节对齐问题,强调了理解内存对齐对于优化代码和提高效率的重要性。
摘要由CSDN通过智能技术生成

结构体用来保存不用类型的数据非常方便,但是计算结构体的大小并不是将变量所占的字节数相加,而是按照一定的对齐规则进行计算结构体的大小的。

由于地址对齐的原因,结构体大小的计算必须满足两条原则:

一、结构体每一个成员相对于结构体首地址的偏移量必须是成员大小的整数倍(0被认为是任何数的整数倍)

二、结构体的总大小必须是所有成员大小的整数倍(数组成员除外,结构体成员除外)

下面通过代码来验证

1、一般的结构体

#include<stdio.h>
#include<stdlib.h>

struct Data{

    char c1;  //1
    char c2;  //1
    int i;     //4  因为偏移量要等于当前成员大小的整数倍,2不是4的整数倍
               //所以2后面要补齐2,所以是1+1+2+4个字节
};

int main()
{
    printf("结构体大小为%d\n",sizeof(struct Data));
    system("pause");
    return 0;
}

在这里插入图片描述
这个结构体大小可以按照对齐规则中的第一条就可以计算

在看下面一个结构体大小该等于多少呢?

#include<stdio.h>
#include<stdlib.h>

struct Data{

    char c1; //1
    int i;   //4 + 3
    char c2;  //1   因为偏移量8是1的整数倍,结构体大小为1+4+3+1 = 9 
              //但是不是所有成员的整数倍,所有要继续向后偏移,所以是12
};

int main()
{
    printf("结构体大小为%d\n",sizeof(struct Data));
    system("pause");
    return 0;
}

在这里插入图片描述
计算这个结构体的大小如果只按结构体对齐原则的第一条,结构体大小就应该为9(9 = 1+4+3+1,3为补齐的字节数),但是结构体的大小为12,那是因为还要满足结构体对齐原则中的第二条,结构体总大小为所有成员大小的整数倍,(9不是4的整数倍),所以要继续向后偏移,直到达到所有成员的整数倍为止。

2、成员包含数组的结构体

#include<stdio.h>
#include<stdlib.h>

struct Data{

    char c;        //1
    int i;         //4+3
    char str[10];  //10  18不是总的整数倍,所以继续偏移到20
};         

int main()
{
    printf("结构体大小为%d\n",sizeof(struct Data));
    system("pause");
    return 0;
}
 

在这里插入图片描述
这个char类型的数组,只需要把它看做十个char连在一起就可以了,加起来就是18,在满足结构体总大小为成员整数倍,(18不是10的整数倍),所以大小就是20

那为什么结构体的总大小必须是所有成员大小的整数倍时数组要除外呢?

#include<stdio.h>
#include<stdlib.h>

struct Data{

    char c;        //1
    int i;         //4+3
    char str[12];  //12  1+4+3+12 = 20,但是不是12的整数倍
};         			//所以继续偏移到24

int main()
{
    printf("结构体大小为%d\n",sizeof(struct Data));
    system("pause");
    return 0;
}

在这里插入图片描述
可以看到当数组大小改为12时,结构体大小还是为20,并不是上面计算出的24,那是因为数组不满足结构体总大小为所以成员的整数倍,如果满足的话就是(1+4+3+12 = 20)但是不是12的整数倍,所以继续偏移到24,但是结构体的真实大小为20。

3、成员包含结构体的结构体

#include<stdio.h>
#include<stdlib.h>

struct Data{

    char c;       //1
    int i;       //4 + 3
    struct s{             //如果只是声明结构体,那么算大小就会忽略结构体,不占空间,变量定义了才占空间
        char c1;    //1
        int j;      //4 + 3
    
    };
    float f;  //16 + 4 = 20,但是不是结构体8的整数倍,如果按照总大小为结构体成员的整数倍的话就是24  
};            //但是运算结果为20,所以当成员是结构体时也不遵循原则二

int main()
{
    printf("结构体大小为%d\n",sizeof(struct Data));
    system("pause");
    return 0;
}

当只是声明结构体,不定义的话运行结果如下:
在这里插入图片描述
当声明了结构体,同时定义了结构体时

struct Data{

    char c;      
    int i;       
    struct s{             //如果只是声明结构体,那么算大小就会忽略结构体,不占空间,变量定义了才占空间
        char c1;          //不同编译器会不一样
        int j;      
    
    }tmp;    //定义结构体变量tmp
    float f;    
}; 

运行结果:
在这里插入图片描述

可以看到,如果只是声明结构体,那么算结构体大小时就会忽略结构体,结构体不占空间,要定义了结构体变量才占内存空间,还应该注意,在这里对于不同的编译器会出现不一样的结果,有的编译器在没有定义变量的时候结构体大小也是20,在定义了结构体大小的时候也是20,有的编译器在没有定义结构体变量时就会忽略结构体。

在这里,里面这个结构体的大小是8,那么结构体大小是否就要向8对齐呢?这个结构体的大小是20,很明显不是8的倍数,(所以原则二中要去掉结构体),所以计算结构体大小时,是把里面这个结构体就单独的看做是一个char,和一个int,不是看做一个整体。

4、成员包含联合体的结构体

#include<stdio.h>
#include<stdlib.h>

struct Data{    //1+4+3+4 = 12

    char ch;   //1
    int i;     //4 + 3
    union{     //联合体大小就是成员中最大类型的大小
        char ch1;
        int j;    //4  

    };

};         

int main()
{
    printf("结构体大小为%d\n",sizeof(struct Data));
    system("pause");
    return 0;
}
 

在这里插入图片描述
在联合体前面的大小为8,因为联合体的大小就是联合体成员中最大类型的大小,这个联合体最大类型为 int ,4个字节,(8是4的整数倍)所以这个结构体大小是8+4 = 12。

5、指定对齐值

a:对齐值小于最大类型成员值

#include<stdio.h>
#include<stdlib.h>
#pragma pack(4)  //指定向4对齐 最大是8
struct Data{    

    char c;   //1
    int i;    //4 + 3
    float f;  //4   到这里是12按照4对齐的话12是4的倍数,所以就是12+8 = 20
                   //如果按照8对齐的话12不是8的倍数,所以要偏移到16,最后大小等于24
    double d;  //12 + 4 + 8 = 24 

};        

int main()
{
    printf("结构体大小为%d\n",sizeof(struct Data));
    system("pause");
    return 0;
}
 

在这里插入图片描述
这里是指定向4对齐,按照4对齐的话在double这一段字节数应该为1+4+3+4 = 12,是4的倍数,在加上double的大小,所以总大小就是12+8 = 20 , 如果是按照8对齐的话,到double前面总大小应该为1+4+3+4 = 12,因为12不是8的整数倍,所以还要向后偏移到16,所以总大小应该为12+4+8 = 24 。

如果去掉 #pragma pack(4) 这一句,结构体就会向 8 对齐,结果就等于24。

b : 对齐值大于最大类型成员值

#include<stdio.h>
#include<stdlib.h>

#pragma pack(10)   //当成员的大小超过了pack规定的大小对齐,那么就按规定的大小对齐
                  //如果最大成员的大小没有超过要求的对齐的话,那还是按照最大成员来对齐
struct Data{    

    char c;   //1
    int i;    //4 + 3
    float f;  //4
    double d;  //12 + 4 + 8 = 24

};        

int main()
{
    printf("结构体大小为%d\n",sizeof(struct Data));
    system("pause");
    return 0;
}
 

在这里插入图片描述
在这里指定的对齐值是向10对齐,那是否就向10对齐呢?从运行结果可以看到不是的,如果按照10对齐的话,结构体的大小应该为30才对,这里结果等于24明显是按照8对齐的。

也就是说当成员的大小超过了pack规定的大小对齐时,那么就按规定的大小对齐,如果最大成员的大小没有超过要求的对齐的话,那还是按照最大成员来对齐。

计算结构体大小时,需要考虑到内存对齐的问题。内存对齐是为了优化内存访问速度而进行的一种技术,它要求不同数据类型的变量在内存中的存储地址要按照一定规则进行对齐。具体来说,每个数据类型都有一个对齐值,即该数据类型变量在内存中存储的起始地址必须是该对齐值的倍数。 在C语言中,可以使用`sizeof`运算符来计算结构体大小,但是这个大小可能会受到内存对齐的影响。为了确保计算出的大小是考虑了内存对齐的,我们可以使用`__alignof__`关键字来获取每个成员变量的对齐值,并手动进行对齐计算。 下面是一个示例代码,演示如何使用内存对齐计算结构体大小: ```c #include <stdio.h> struct my_struct { char c; int i; double d; }; int main() { struct my_struct s; size_t size = sizeof(s); size_t align_c = __alignof__(s.c); size_t align_i = __alignof__(s.i); size_t align_d = __alignof__(s.d); size_t align = align_c > align_i ? align_c : align_i; align = align > align_d ? align : align_d; size_t padding_c = align - sizeof(s.c) % align; size_t padding_i = align - sizeof(s.i) % align; size_t padding_d = align - sizeof(s.d) % align; size_t padding = padding_c + padding_i + padding_d; size += padding; printf("Size of my_struct: %zu\n", size); return 0; } ``` 在上面的示例代码中,我们首先定义了一个`my_struct`结构体,其中包含一个`char`类型的成员变量`c`、一个`int`类型的成员变量`i`和一个`double`类型的成员变量`d`。然后我们使用`sizeof`运算符计算结构体大小,并使用`__alignof__`关键字获取每个成员变量的对齐值。接下来,我们计算结构体需要进行的对齐和填充,最后将填充的大小加入到结构体大小中,输出计算出的结构体大小。 需要注意的是,对齐值可能会因为编译器和操作系统的不同而有所差异,因此在实际应用中需要谨慎处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值