准确计算编译器在结构中添加的填充
Accurately calculate the compiler added padding in a struct
我目前有以下结构
struct foo {
u32 bar;
u8 baz[1];
}
由于填充,sizeof(struct foo)结果为8。
如果我想要结构的大小是准确的(在本例中为5),我将如何以"可缩放"的方式进行计算(即不执行类似sizeof(foo.bar) + sizeof (foo.baz)
的操作)?
在标准C中,如果不在源代码中列出结构成员的大小,就无法计算结构成员的总大小(这可以在预处理器功能的帮助下完成,也可以使用其他代码[在最终代码的编译时编译并执行]来生成最终代码)。这是因为标准C没有提供任何方法来迭代结构的成员,或者在不使用其成员名称的情况下检查其组成。
我会对C++做出同样的声明,只是模板之类的东西可能会有一些可怕的黑客攻击。我倾向于这是不可能的,但我还没有研究最新C++标准中的所有新功能。
在任何情况下,计算总大小都不足以实现序列化和反序列化成员的既定目的(即,将成员转换为网络包中的字节,反之亦然),因为您仍然需要单独转换成员,而不仅仅是知道其大小的总和。
实现目标的选项包括:
- 使用特定于实现的功能来打包结构,使其不包含填充。然后,您可以写入和读取结构的字节,以执行序列化和反序列化。您仍然需要确保发送和接收系统之间的类型匹配(相同宽度的整数、相同格式的浮点值、相同的字符集编码、等)
- 编写用于运行时处理所需类型的代码。它将以某种格式(如类型列表)对结构进行描述,并为每种类型都有大小写。每个案例都包含该类型的编译时代码(例如
sizeof(float)
),但代码会在运行时在案例之间进行调度。因此,您需要按照此代码使用的格式准备结构的描述
"…以可扩展的方式?"——不行,C没有内存布局语义。
注:我从来没有找到一个编译器为"sizeof(T)",提供错误的值
编译器告诉你一个由N个struct-foo对象组成的数组,正如您在这里介绍的,将占用8*N个字节。
然而,我在一个项目中,编码为强制使用"适当",预期的,我们可以说是物体的最紧凑的"包装",而不使用杂注。(我认为实用主义者非便携式。然而,我研究过的每一个编译器其中包含了pragmas,似乎提供了极好的结果。这个pragma偶尔拼写不一样)
换句话说,如果您真的需要一个5字节的foo对象,那么实现它是可能的,而且我认为相对容易。但是,如果你对象foo增长到有很多字段。
在我的嵌入式软件项目中,远程处理器在不同的构建机器上使用了不同的处理器和编译器,他们使用了编译器的pragma包。
我们的软件必须描述一个符合其包装的对象,因为数千个子系统已经发货。我们的代码必须能够进行交互。这些系统通过专用背板发送的二进制数据包进行通信。(C和C++都没有提供内存布局符号。Ada提供了,但有人在乎了吗?)
我们尝试了许多解决方案,每个方案都有赞成和反对。
以下是根据5种成功机制中的1种而设计的。。。这个很容易记住。
两个步骤:
- 使用字节数组声明对象
- 添加适当的setter和getter,每个字段各1个
注意:您将学习endian-ness,查看宏的ntoh和hton
namespace fooNameSpace
{
struct foo
{
private:
uint8_t pfd[5]; //packed_foo_data
public:
void barSet(uint32_t value)
{
pfd[0] = static_cast<uint8_t>((value & 0xff000000) >> 24); // msbyte
pfd[1] = static_cast<uint8_t>((value & 0x00ff0000) >> 16);
pfd[2] = static_cast<uint8_t>((value & 0x0000ff00) >> 8);
pfd[3] = static_cast<uint8_t>((value & 0x000000ff) >> 0); // lsbyte
}; // you will need to fix for endian of target
uint32_t barGet(void)
{
uint32_t value = 0;
value |= pfd[0] << 24; // msbyte
value |= pfd[1] << 16;
value |= pfd[2] << 8;
value |= pfd[3] << 0; // lsbyte
return (value);
}; // fix for endian of target
void bazSet(uint8_t value) { pfd[4] = value; }
uint8_t bazGet(void) { return pfd[4]; };
};
}
我的一些团队试图创建一个联盟。。。发现当工会必须匹配远程编译器、目标、端序在本地主机和编译器上。有点困惑模拟器。
- 将成员变量添加到共享库中的类中,不会破坏二进制兼容性吗
- 如何在C++中从两个单独的for循环中添加两个数组
- 在c++中用vector填充一个简单的动态数组
- POCO::PostgreSQL:如何将std::vector支持添加到`Binder::bind`
- 如何仅为一个函数添加延迟
- 如何使用用户输入在C++中正确填充2D数组
- 如何防止 c++ 在从浮点型转换为双精度型(不适用于 IO)时添加额外的小数?
- C++添加尾随 0 的 ostream 填充
- 在 pytorch c++ API 中添加填充
- QT-仅用密钥填充QMAP,然后添加每个键的值
- 为什么可以添加填充使您的循环更快
- 如何使用 << 添加一个空格来填充带有 ofstream 的减号?
- 准确计算编译器在结构中添加的填充
- 为什么为结构的多个数据成员而不是单个成员添加填充
- 为什么要在 C++ 结构中添加填充物
- WTableView中的QueryModel:请举例说明如何添加一行并用刚刚创建的新记录填充它
- 如何将填充零添加到写入流的数字中
- Visual C# - 引用 > 添加引用... > COM:该列表是如何填充的?
- AES_cbc_encrypt是否添加填充
- 如何在rijndael.h中添加PKCS7填充