C-结构体对齐

结构体对齐(Struct Alignment):是计算机编程中的一个概念,通常用于描述编译器如何安排结构体的成员在内存中的存储方式。

在很多计算机体系结构中,访问未对齐的数据可能会导致性能下降,甚至是程序崩溃。为了优化内存访问,编译器会对结构体进行对齐,即按照某种规则将结构体的成员排列在内存中,以便于有效地访问。这种对齐通常是根据硬件平台的要求来进行的。

具体对齐值的设置以及对齐值的用处我们在实际例子中进行阐述。

举个例子,设置如下结构体:

typedef struct _INFO {
    char szValue[6];
    float fValue;
    double dbValue;
    short sValue;
    int nValue;
    char cValue;
}Info,*Info;

定义完结构体后使用sizeof()计算该结构体的大小:

int main()
{   
    printf("sizeof Info:%d", sizeof(Info));
    system("pause");
    }

这个结构体的大小是如何计算而来的呢?

这个时候就需要引出一个概念

成员偏移量:
结构体成员偏移量(Struct Member Offset)指的是结构体中每个成员相对于结构体起始地址的偏移量。这个偏移量表示了该成员在内存中的存储位置。

那么现在我上面的结构体进行初始化,根据内存中的内容进行计算。

Info info = { "wolve",12.5f,100.5,0xAAAA,0xBBBBBBBB,'c' };

打个断点,进行内存查看。

内存的内容如下(以32位的格式进行排列)

实际的内容在c(ascii = 63)处结束;

77 6f 6c 76
65 00 cc cc
00 00 48 41
cc cc cc cc 
00 00 00 00 
00 20 59 40 
aa aa cc cc 
bb bb bb bb 
63

发现目前结构体的存储内容所需要的实际内存只有33字节,并且编译器在某些地方会自动补入cc字符进行填充。这个结果其实就是由结构体的成员偏移量引起的。

成员偏移量的计算公式
member offset % min(ZpValue,sizeof(member type)) == 0;

member offset:表示结构体成员偏移量;

ZpValue:表示对齐值;

对齐值可以在Visual Studio中进行设置。

MVSC默认对齐是8

min():取后面数列最小值;

sizeof(member type):成员类型大小;

min(ZpValue, sizeof(member type))表示 ZpValue 和成员类型大小中的较小值。

% : 表示取模运算符,即取偏移量除以最小对齐值和成员类型大小之间的较小值的余数。

==0:表示取模的结果必须为 0,即偏移量必须能够整除最小对齐值和成员类型大小之间的较小值。

介绍完公式后继续以上述内存中的内容为例子进行演示:
内存:
77 6f 6c 76
65 00 cc cc
00 00 48 41
cc cc cc cc 
00 00 00 00 
00 20 59 40 
aa aa cc cc 
bb bb bb bb 
63
结构体:
typedef struct _INFO { //格式:成员偏移量
    char szValue[6];   //0
    float fValue;      //理论偏移量:6;实际偏移量:member offset % min(8,4) == 0; 在计算的时候先将理论偏移量带入(member offse) =>  6 % 4 == 0? 显然6除以4取余不为0,那么这个时候就持续在理论偏移量上+1,直到偏移量为8的时候,余为0,这个时候float类型成员实际成员偏移量为8(在上述内存的内容中已经体现);接着在该基础上继续计算后续几个类型的成员偏移量。
    double dbValue;  //理论偏移量:8+4=12,实际偏移量:16  因为16 % min(8,8) == 0(所以double在第17字节开始存储)
    short sValue; //理论偏移量:16+8=24 ,实际偏移量:24 因为 24 % min(8,2) == 0(所以short在第15字节位置开始存储)
    int nValue;  //理论偏移量:24+2=26,实际偏移量:28 因为 28 % min(8,4) == 0(所以int成员在第28字节位置开始存储)
    char cValue; //理论偏移量:28+4=32,实际偏移量:32 因为 32 % min(8,1) == 0(所以int成员在第28字节位置开始存储)
}Info,*Info;
​
最后实际偏移32 + 1 (char成员大小) == 33 (得到实际存储成员所用的大小)
​
偏移量:在该数据前面存储着多大的数据

但是为什么sizeof()函数在计算结构体大小的时候得到的值是40呢?

原因在计算整个结构体大小的时候还需要满足另外一个公式:

Struct size % Max(Member type) == 0
​
​
这个表达式描述了结构体的大小必须是结构体中最大成员的大小的倍数。
​
 Struct size 表示结构体的总大小。
 Max(Member type) 表示结构体中成员类型的最大大小。
​
因此,这个表达式确保了结构体的大小能够容纳结构体中最大的成员,并且结构体的布局能够在内存中正确对齐,从而避免了浪费内存空间和可能导致的对齐问题。
这个时候我们接着带入计算
Struct size = 初步等于33(也就是刚才计算出来的结构体实际存储空间大小)
Max(Member type) = 8 (该结构体中成员最大类型宽度)
​
33 % 8 == 0 ?    很显然33除以8余数不为0,那么还是一样持续+1,指导Struct Size结构体大小为40时可以整除8;
所以sizeof()计算出该结构体大小为40。

最后,该结构体实际在内存中存储为:

77 6f 6c 76 65 00 cc cc 00 00 48 41 cc cc cc cc 00 00 00 00 00 20 59 40 aa aa cc cc bb bb bb bb 63 cc cc cc cc cc cc cc cc

C语言中的结构体对齐是一个经常被问到的面试题。这里我来简单解释一下。 在C语言中,结构体是一种用户自定义的数据类型,它允许我们把多个不同类型的变量组合在一起,以便更方便地管理和使用它们。 在内存中,结构体的存储空间是按照成员变量的顺序依次分配的。但是,为了提高访问效率,编译器会对结构体进行对齐操作,也就是将结构体的起始地址调整为成员变量大小的整数倍。 例如,一个包含三个成员变量的结构体: ``` struct Test { char a; int b; short c; }; ``` 在32位系统中,char占1个字节,int占4个字节,short占2个字节,所以这个结构体的大小应该是1 + 4 + 2 = 7字节。但是,如果我们直接按顺序分配,结构体的起始地址是一个奇数,这样访问效率会降低。因此,编译器会在结构体的成员变量之间插入一些字节,使得结构体的起始地址是4的倍数。这样,结构体的大小就会变成12字节。 具体的对齐规则和字节数是由编译器决定的,不同的编译器可能会有不同的规则。但是,大多数编译器都会使用和本例相似的规则。 在面试中,经常会出现一些关于结构体对齐的问题,例如: 1. 结构体的大小是多少? 2. 结构体成员变量的顺序会影响结构体的大小吗? 3. 如何使用#pragma pack指令来控制结构体对齐? 4. 等等。 对于这些问题,我们需要对结构体对齐有一个深刻的理解,才能够正确回答。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值