结构体内存对齐问题
- 结构体内成员按照声明顺序存储,第一个成员地址和整个结构体地址相同。
- 未特殊说明时,按结构体中size最大的成员对齐(若有double成员,按8字节对齐。)
c++11以后引入两个关键字 alignas
与 [alignof]
。其中alignof
可以计算出类型的对齐方式,alignas
可以指定结构体的对齐方式。
但是alignas
在某些情况下是不能使用的,具体见下面的例子:
// alignas 生效的情况
struct Info {
uint8_t a;
uint16_t b;
uint8_t c;
};
std::cout << sizeof(Info) << std::endl; // 6 2 + 2 + 2
std::cout << alignof(Info) << std::endl; // 2
struct alignas(4) Info2 {
uint8_t a;
uint16_t b;
uint8_t c;
};
std::cout << sizeof(Info2) << std::endl; // 8 4 + 4
std::cout << alignof(Info2) << std::endl; // 4
alignas
将内存对齐调整为4个字节。所以sizeof(Info2)
的值变为了8。
// alignas 失效的情况
struct Info {
uint8_t a;
uint32_t b;
uint8_t c;
};
std::cout << sizeof(Info) << std::endl; // 12 4 + 4 + 4
std::cout << alignof(Info) << std::endl; // 4
struct alignas(2) Info2 {
uint8_t a;
uint32_t b;
uint8_t c;
};
std::cout << sizeof(Info2) << std::endl; // 12 4 + 4 + 4
std::cout << alignof(Info2) << std::endl; // 4
若alignas
小于自然对齐的最小单位,则被忽略。
- 如果想使用单字节对齐的方式,使用
alignas
是无效的。应该使用#pragma pack(push,1)
或者使用__attribute__((packed))
。
#if defined(__GNUC__) || defined(__GNUG__)
#define ONEBYTE_ALIGN __attribute__((packed))
#elif defined(_MSC_VER)
#define ONEBYTE_ALIGN
#pragma pack(push,1)
#endif
struct Info {
uint8_t a;
uint32_t b;
uint8_t c;
} ONEBYTE_ALIGN;
#if defined(__GNUC__) || defined(__GNUG__)
#undef ONEBYTE_ALIGN
#elif defined(_MSC_VER)
#pragma pack(pop)
#undef ONEBYTE_ALIGN
#endif
std::cout << sizeof(Info) << std::endl; // 6 1 + 4 + 1
std::cout << alignof(Info) << std::endl; // 6
确定结构体中每个元素大小可以通过下面这种方法:
#if defined(__GNUC__) || defined(__GNUG__)
#define ONEBYTE_ALIGN __attribute__((packed))
#elif defined(_MSC_VER)
#define ONEBYTE_ALIGN
#pragma pack(push,1)
#endif
/**
* 0 1 3 6 8 9 15
* +-+---+-----+---+-+-------------+
* | | | | | | |
* |a| b | c | d |e| pad |
* | | | | | | |
* +-+---+-----+---+-+-------------+
*/
struct Info {
uint16_t a : 1;
uint16_t b : 2;
uint16_t c : 3;
uint16_t d : 2;
uint16_t e : 1;
uint16_t pad : 7;
} ONEBYTE_ALIGN;
#if defined(__GNUC__) || defined(__GNUG__)
#undef ONEBYTE_ALIGN
#elif defined(_MSC_VER)
#pragma pack(pop)
#undef ONEBYTE_ALIGN
#endif
std::cout << sizeof(Info) << std::endl; // 2
std::cout << alignof(Info) << std::endl; // 1
这种处理方式是alignas
处理不了的。
评论(2)
帅地哥,本篇存在错误,S2和S3应该都是占24字节大小
重新找了个更加详细的答案