假设64位机器:
如果我从A类开始:
struct A
{
int* a1; //8
int* a2; //8
uint16_t a3; //2
uint16_t a4; //2
uint32_t a5; //4
uint32_t a6; //4
uint32_t a7; //4
};
现在所有单个成员都有正确的对齐方式,A
的大小为32个字节,从a5
到a1
的偏移量为20个字节
现在,如果我尝试按如下方式重构:
struct A_part1
{
int* a1; //8
int* a2; //8
uint16_t a3; //2
uint16_t a4; //2
};
struct A_new
{
A_part1 a1;
uint32_t a5; //4
uint32_t a6; //4
uint32_t a7; //4
};
现在A_new
的大小为40个字节,因为A_part1
最多填充24个字节,A_new
随后从36个字节填充到40个字节。
据推测,编译器正在尝试确保连续的A_part1
对齐。
如果我知道我只会在A_part1
使用A
,那么我必须在pragma
1上使用a_part
包来确保A_new
与原始A
具有相同的特征?
答案 0 :(得分:2)
你是对的:标准C ++中没有办法指定struct
的填充布局,因此你会遇到像#pragma pack
这样的特定于编译器的控制机制。
答案 1 :(得分:1)
如果我知道我只会在A中使用A_part1,那么我是唯一的选择 必须在a_part1上使用pragma包来确保A_new 具有与原始A?
相同的特征
没有。 pragma不是指定布局和填充的唯一方法。
在我曾经使用过的嵌入式软件中,我们找到了几种解决pragma问题的方法。
C和C ++都没有提供内存布局语义。
但是,一种易于理解的技术,至少在逐字段的基础上,是明确地编码字节驻留在类或结构中的位置。通过重构和添加小字节移动方法,工作量大大减少。
注意:七个字段很小,因此很“简单”。五十个领域令人厌倦。数百个字段是“让我们编写一些代码来编写代码”。你的容忍度会有所不同。
该技术将字节显式地移动到所需的字节数据中。优点:1)没有填充(除非你想要它)。 2)对编译器选项的变化不敏感。 3)便携式。
小例子(编译,但未测试)(注意:任意字节序选择,我们使用传统的字节序转换)
class A
{
// int* a1; //8 0..7
// int* a2; //8 8..15
// uint16_t a3; //2 16..17
// uint16_t a4; //2 18..19
//
// uint32_t a5; //4 etc
// uint32_t a6; //4
// uint32_t a7; //4
// ...
// modify field a3
void a3(uint16_t val) {
// if needed, correct val for destination endianess ------vvv
data[Oa3+0] = static_cast<uint8_t>((val >> 0) & 0xff); // LSB
data[Oa3+1] = static_cast<uint8_t>((val >> 8) & 0xff); // MSB
}
// access field a3
uint16_t a3() {
// if needed, correct val for endianess ---------------------vvv
uint16_t val = static_cast<uint16_t>((data[Oa3+0] << 0) + // LSB
(data[Oa3+8] << 8)); // MSB
return (val);
}
// continue for each field
private:
enum {
Oa1 = 0, // Offset a1
Oa2 = 8,
Oa3 = 16
//... etc
};
uint8_t data[(8+8+2+2+(3*4))];
};