C基础--内存对齐问题(结构体对齐)

问题现象

  在调试一个软件功能时,发现一个结构体对齐的问题,以前没有太关注,现在把它总结出来。先看示例:

  • 结构体1:
typedef struct
{
    
    
	char magic[4];
	uint32_t crc32;
	uint32_t lenght;
	uint16_t ver;
	uint16_t IFrameCnt;
	uint32_t startTime;
	uint32_t startPts;
	uint32_t sliceTimeLen;
	uint32_t sliceLen;
	uint8_t res[16];
}RecSliceIndexHeader_t;

  RecSliceIndexHeader_t的大小为:sizeof(RecSliceIndexHeader_t) = 48

  • 结构体2:
typedef struct
{
    
    
	uint8_t codecType;
	uint8_t frameRate;
	uint8_t colorDepth;
	uint8_t frameInterval;
	int32_t quality;
	uint32_t bitRate;
	uint16_t width;
	uint16_t height;
	uint8_t isEncrypt;    
	uint8_t res[15];
}RecSliceVideoParam_t;

  通过sizeof得到结构体RecSliceVideoParam_t的大小为:sizeof(RecSliceVideoParam_t) = 32;

  • 结构体3:
typedef struct
{
    
    
	uint8_t codecType;
	uint8_t channel;
	uint32_t sampleRate;
	uint16_t bitsPerSample;
	uint8_t res[16];
}RecSliceAudioParam_t;

  通过sizeof得到结构体RecSliceAudioParam_t的大小为:sizeof(RecSliceAudioParam_t) = 28;

  • 结构体4:
typedef struct
{
    
    
	uint64_t time;
	uint32_t offset;
	uint32_t IFrameLen;
	uint8_t res[16];
}RecIFrameInfo_t;

  通过sizeof得到结构体RecIFrameInfo_t的大小为:sizeof(RecIFrameInfo_t) = 32;

  • 结构体5:
typedef struct
{
    
    
	RecSliceIndexHeader_t head;
	RecSliceVideoParam_t video;
	RecSliceAudioParam_t audio;
	RecIFrameInfo_t iFrame[1];
}RecSliceIndexInfo_t;

  通过sizeof得到结构体RecSliceIndexInfo_t的大小为:sizeof(RecSliceIndexInfo_t) = 144;
  ((unsigned long)(&((RecSliceIndexInfo_t*)0)->IFrameInfo[0])) = 112;
  sizeof(RecSliceAudioParam_t) +sizeof(RecSliceVideoParam_t)+sizeof(RecSliceIndexHeader_t) = 108

问题分析

问题1

  关于结构体3,它在内存中的实际对齐方式是:

typedef struct
{
    
    
	uint8_t codecType;
	uint8_t channel;
	uint32_t sampleRate;
	uint16_t bitsPerSample;
	uint8_t res[16];
}RecSliceAudioParam_t;

  它实际的对齐方式如下:

typedef struct
{
    
    
	uint8_t codecType;
	uint8_t channel;
	uint8_t res1[2];    // 编译器加对齐的字节
	uint32_t sampleRate;
	uint16_t bitsPerSample;
	uint8_t res1[2];    // 编译器加对齐的字节
	uint8_t res[16];
}RecSliceAudioParam_t;

  所以最终输出的值为:sizeof(RecSliceAudioParam_t) = 28;这个问题的关键是君正的字节对齐方式问题,在结构体内,它只要碰到4个字节的变量就会将结构体做4字节对齐。

问题2:

  关于结构体5,sizeof(RecSliceAudioParam_t)+sizeof(RecSliceVideoParam_t)+sizeof(RecSliceIndexHeader_t) 的值为108,我们发现RecIFrameInfo_t的成员uint64_t time的大小为8字节,因此导致108无法被8整除,在RecSliceIndexInfo_t 的IFrameInfo元素会填充4字节。因此成员IFrameInfo前面的元素整个大小为112字节。这个问题在组合结构体尤其需要关注。

改进措施

  在没有#pragma pack这个宏的声明下,结构体的对齐遵循下面四个原则:

  • 第一个成员的首地址为0。
  • 结构体的起始地址能够被其最宽的成员大小整除;
  • 结构体每个成员相对于起始地址的偏移能够被其自身大小整除,如果不能则在前一个成员后面补充字节。
  • 结构体总体大小能够被最宽的成员的大小整除,如不能则在后面补充字节。
    一般来说,不太建议使用#pragma pack这个宏,从找的资料看看,使用这个宏强制对齐在不同的平台可能会导致异常的BUG或者死机。

一般来说定义结构体遵循如下步骤,定义芯片平台对齐的字节数为PPB(以4字节对齐芯片平台举例,PPB=4):

  • 在单个结构体中,前面定义的成员遇到PPB大小的成员时,需要手动添加保留直接做对齐,或者调整成员的顺序,下面举例说明:
    在这里插入图片描述
    比如定义一个结构体:
      里面有x1、x2、x3、x4、x5、x6共6个元素,其中x1、x2、x3为char,x4为int类型即大小为4,和PPB相同。那么结构体定义的时候就要确保x1+x2+x3的大小是x4的整数倍(即4的整数倍),即需要在x1、x2、x3和x3之后再增加一个字节保证在x4之前对齐;(总的来说对应的是结构体的对齐遵循下面四个原则的第三点);
  • 在复合结构体中(结构体中包含结构体)
      上面问题遇到的问题,主要是复合结构遇到的问题,RecSliceIndexInfo_t的IFrameInfo是RecIFrameInfo_t结构体,这个RecIFrameInfo_t的time是8字节的,前面的所有长度为108,因此不够8整除,需要在前面三个成员加上4字节做对齐,即112字节。所以复合结构体中某个结构体有大于PPB的成员,需要仔细检查整个结构体的对齐问题。

猜你喜欢

转载自blog.csdn.net/jisuanji111111/article/details/127886781