C++将结构强制转换为 std::矢量<char>内存对齐



>我正在尝试将结构转换为字符向量。 我想发送在 std::vector 中转换的结构,抛出一个 UDP 套接字并将其投射回另一侧。这是我的结构,带有 PACK 属性。

#define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop) )
PACK(struct Inputs
{
uint8_t structureHeader;
int16_t x;
int16_t y;
Key inputs[8];
});

以下是测试代码:

auto const ptr = reinterpret_cast<char*>(&in);
std::vector<char> buffer(ptr, ptr + sizeof in);
//send and receive via udp
Inputs* my_struct = reinterpret_cast<Inputs*>(&buffer[0]);

问题是: 除了我的uint8_t或int8_t外,一切正常。 我不知道为什么,但无论何时何地,我在结构中放置一个 1Bytes 值, 当我将其投射回时,该值不可读(但其他值是) 我尝试只输入 16 位值,即使使用 最大值,因此所有位都没问题。

我认为这与内存中字节的对齐有关,但我无法弄清楚如何使其工作。

谢谢。

我正在尝试将结构转换为字符向量。

不能将任意对象强制转换为矢量。 您可以将对象强制转换为char数组,然后将该数组复制到向量中(这实际上是您的代码正在执行的操作)。

auto const ptr = reinterpret_cast<char*>(&in);
std::vector<char> buffer(ptr, ptr + sizeof in);

第二行定义一个新向量,并通过将表示对象的字节复制到其中来初始化它。 这是合理的,但它与你所说的你想做的事情不同。

我认为这与内存中字节的对齐有关

这是很好的直觉。 如果您没有告诉编译器打包结构,它将插入填充字节以确保每个字段从其自然对齐开始。 操作不可逆的事实表明,接收端的打包方式并不完全相同。 您确定接收程序具有完全相同的包装指令和结构布局吗?

在 x86 上,您可以使用未对齐的数据,但每当访问未对齐的成员变量时,都可能需要付出很大的性能成本。 将打包设置为 1,并且第一个字段为奇数大小,您可以保证下一个字段将未对齐。 我敦促你重新考虑这一点。 设计结构,使所有字段都落在其自然对齐边界处,并且无需调整包装。 这可能会使您的结构更大一些,但它将避免所有对齐和性能问题。

如果要省略线路格式的填充字节,则必须将相关字段逐字节复制到线路格式,然后在接收端将它们复制回去。

关于以下方面的旁白:

#define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop) )

以下划线和大写字母或两个下划线开头的标识符保留用于"实现",因此您可能不应使用__Declaration__作为宏的参数名称。 ("实现"是指编译器、标准库和编译器所需的任何其他运行时位。

1

Vector 类动态分配了内存,并在内部使用指针。所以你不能发送向量(但你可以发送底层数组)

阿拉伯数字

SFML有一个很棒的类来做到这一点,叫做sf::p acket。它是免费的、开源的和跨平台的。

我最近正在开发一个用于其他个人项目的个人跨平台套接字库,我最终退出了 SFML。要测试的东西太多了,我把所有的时间都花在测试上,以确保东西有效,而不是在我想做的实际项目上完成任何工作。

3

Memcpy是你最好的朋友。它被设计为便携式,您可以利用它来发挥自己的优势。

您可以使用它进行调试。 将您想要看到的内容放入 char 数组中,并检查它是否与预期匹配。

为了避免自己进行大量的健壮性测试,请将自己限制为仅字符、32 位整数和 64 位双精度。您使用的是不同的编译器?结构打包取决于编译器和体系结构。如果必须使用打包结构,则需要保证打包在您将使用的所有平台上按预期工作,并且所有平台都具有相同的字节序。显然,这就是您遇到的麻烦,很抱歉我无法为您提供更多帮助。我会建议定期序列化,如果我尝试制作便携式插座,我肯定会避免结构打包。

如果你能做出我提到的那些保证,那么在 Linux 上发送真的很容易。

// POSIX
void send(int fd, Inputs& input)
{
int error = sendto(fd, &input, sizeof(input), ..., ..., ...);
...
}

Winsock2 使用字符* 而不是 void* :(

void send(int fd, Inputs& input)
{
char buf[sizeof(input)];
memcpy(buf, &input, sizeof(input));
int error = sendto(fd, buf, sizeof(input), ..., ..., ...);
...
}

您是否尝试了以下最简单的方法:

unsigned char *pBuff = (unsigned char*)&in;
for (unsigned int i = 0; i < sizeof(Inputs); i++) {
vecBuffer.push_back(*pBuff);
pBuff++;
}

这将适用于包和非包,因为您将迭代大小。

相关内容

最新更新