繁体   English   中英

C++ 将结构转换为 std::vector<char> 内存对齐

[英]C++ casting a struct to std::vector<char> memory alignment

我正在尝试将结构转换为字符向量。 我想发送我在 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 值,当我将其转换回该值时,该值不可读(但其他值是)我尝试只放置 16bits 值,即使使用最大值所以所有位都可以。

我认为这与内存中字节对齐有关,但我不知道如何使其工作。

谢谢。

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

您不能将任意对象强制转换为向量。 您可以将对象转换为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 类具有动态分配的内存并在内部使用指针。 所以你不能发送向量(但你可以发送底层数组)

2

SFML 有一个很好的类来做这个,叫做 sf::packet。 它是免费的、开源的、跨平台的。

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

3

memcpy 是你最好的朋友。 它旨在便携,您可以充分利用这一点。

你可以用它来调试。 memcpy 将您想要查看的内容放入 char 数组并检查它是否符合您的预期。

4

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

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

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

winsock2 使用 char* 而不是 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++;
}

这对打包和非打包都适用,因为您将迭代 sizeof。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM