位域与结构体的内存对齐

一. 一般结构体对齐规则

struct example   
{
	char a;     
	short b;     
	char c;    
}
  1. 对齐参数的确定:例如结构体example中先后定义了三个变量char a;short b;char c;对于变量a、c,它的自身对齐参数为1,而#pragma pack(n)值默认为4,则a的对齐参数为1;b的自身对齐参数为2,而#pragma pack(n)的默认值为4,则b的对齐参数为2。由于a的最终对齐参数为1,b的最终对齐参数为2,那么两者较大者是2,然后再拿2和#pragma pack(n)作比较,取较小者作为对齐参数,也就是2,即意味着结构体最终的大小必须能被2整除。
    总结:
    a、c 最终对齐参数为 1
    b 最终对齐参数为2
    结构体最终对齐参数为2
    注:gcc中默认#pragma pack值为4,最大也为4

  2. 结构体每个成员相对结构体首地址的偏移量(offset)是对齐参数的整数倍,如有需要会在成员之间填充字节。编译器在为结构体成员开辟空间时,首先检查预开辟空间的地址相对于结构体首地址的偏移量是否为对齐参数的整数倍,若是,则存放该成员;若不是,则填充若干字节,以达到整数倍的要求。

    1.1 结构体起始位置一定是内存对齐(对齐字节数可能默认为4字节、8字节,也可能是用户指定的1字节、2字节…)的,因此第一个结构体成员存放位置无需计算
    1.2 根据规则1,第二个结构体的位置就需要与自身默认对其规则来计算,各内置类型的默认对其大小如下:
    在这里插入图片描述

struct example   //假设结构体起始地址为0x00001001
{
	char a;      //a地址等于结构体地址=0x00001001,结束地址=0x00001001 占1字节
	short b;     //b默认对其字节为2,因此b起始地址需再偏移一个地址才能整除2,因此=0x000010002,结束地址=0x000010003 占2字节,上方填充1字节
	char c;     //char默认1字节对其,因此不需偏移,起始地址=0x000010004,结束地址=0x000010004,占1字节
}
到这里结构体至少占用 1+1+2+1 = 5字节
  1. 结构体变量所占空间的大小是对齐参数大小的整数倍。如有需要会在最后一个成员末尾填充若干字节使得所占空间大小是对齐参数大小的整数倍。

    规则0中已明确该结构体的对其参数为2,但是规则1后,结构体内存占用5字节,不能整除2,因此c存储位置下面需多占一个字节用于补齐,因此最终的内存占用为:
    |-–char a(1字节)—|—对齐偏移 (1字节)—|—short b(2字节)—|—char a(1字节)—|—对齐偏移 (1字节)—|

二. 位域结构体对齐规则

当相邻位域成员的类型相同时,如果它们的位宽之和小于类型大小,那么后面的成员紧邻前一个成员存储;如果它们的位宽之和大于类型大小,那么后面的成员将存在下一个类型大小的空间。

  1. GCC无论相邻位域成员的类型相同或者不同,如果它们的位宽之和小于类型大小,那么后面的成员紧邻前一个成员存储;如果它们的位宽之和大于类型大小,那么后面的成员将存在下一个类型大小的空间。也就是说任何情况下都是压缩型排布
    而不是与一般结构体成员那样需要考虑对齐。

  2. 整个结构体的对齐仍旧满足一般结构体对齐规则2,即找到结构体的对齐参数。对齐参数的确认仍旧同一般结构体的规则0中结构体对齐参数的确认方法。

struct example   
{
	char a:4;     
	short b:2;     
	char c:2;    
}

结构体example中,按照压缩规则,三个成员可放在同一字节中,因此实际内存为1字节;按照规则2,short b的自身对齐参数最大,且小于默认#pragma pack值4,因此结构体的对齐参数确定为2.最终整个结构体的大小为2

#pragma pack(1)
struct example   
{
	char a:4;     
	short b:2;     
	char c:2;    
}

那么结构体的对其参数为1,整个结构体的大小为1字节

同理

//#pragma pack(1)
struct example   
{
	char a:4;     
	int b:2;     
	char c:2;    
}

#pragma pack(4)时: 结构体对齐参数=4,结构体占用大小4字节
#pragma pack(1)时: 结构体对齐参数=1,结构体占用大小1字节

  1. 位域结构体中包含非位域成员时会视情况进行压缩。即对位域成员变量压缩,而不压缩非位域成员变量(不压缩成员则按照一般结构体成员对齐规则来存放)
struct example   
{
	char a:4;   //a占四个bit  
	int b;      //b需按照自身对齐参数与#pragma pack中的最小值来对齐,所以a后需填充3个byte + 4bit
	char c:2;   //c本身只占2bit,但是考虑到结构体整体对齐参数为4,则向下补齐6bit + 3byte
}

以上结构体总大小为 12byte

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
结构体字节对齐引起的访问越界问题通常出现在以下情况下: 1. 结构体中存在字节对齐的成员变量,访问该成员变量时,可能会访问到结构体之外的内存空间。例如,如果某个成员变量的长度是4字节,但该结构体的字节对齐方式是8字节,那么访问该成员变量时,可能会访问到结构体之外的4字节内存空间,从而导致访问越界。 2. 结构体中存在数组类型的成员变量,访问该数组时,可能会访问到数组之外的内存空间。例如,如果一个数组的长度是10,但该结构体的字节对齐方式是16字节,那么访问该数组时,可能会访问到数组之外的6字节内存空间,从而导致访问越界。 为了避免结构体字节对齐引起的访问越界问题,可以采取以下措施: 1. 使用编译器提供的或自定义的对齐方式,以确保结构体中成员变量的对齐方式符合要求。 2. 避免在结构体中使用数组类型的成员变量,或者使用动态内存分配等方法来管理数组,以确保数组访问不会越界。 3. 避免在结构体中使用位域类型的成员变量,或者使用位运算等方法来确保位域访问不会越界。 4. 在编写代码时,注意检查结构体中的成员变量访问是否越界,可以使用断言等方法来检查访问边界。 总之,结构体字节对齐引起的访问越界问题需要引起重视,需要在编写代码时注意结构体成员变量的对齐方式和访问边界,以确保程序的正确性和稳定性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值