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

时间:2018-01-19 16:11:21

标签: c++ vector struct casting reinterpret-cast

我正在尝试将结构转换为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位值,即使使用它也可以正常工作 最大值,所以所有位都可以。

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

谢谢。

3 个答案:

答案 0 :(得分:1)

1

vector类具有动态分配的内存并使用内部指针。所以你不能发送矢量(但你可以发送底层数组)

2

SFML有一个很好的类,用于执行此操作,称为sf :: packet。它是免费的,开源的,跨平台的。

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

3

memcpy是你最好的朋友。它设计为便携式,您可以利用它。

您可以使用它进行调试。 memcpy你想要看到的一个char数组,并检查它是否符合你的期望。

4

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

如果你能做出我提到的那些保证,在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), ..., ..., ...);
    ...
}

答案 1 :(得分:0)

  

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

您无法将任意对象强制转换为矢量。您可以将对象转换为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__作为宏的参数名称。 (“实现”是指编译器,标准库以及编译器所需的任何其他运行时位。)

答案 2 :(得分:0)

您尝试过以下最简单的方法吗?

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

这将适用于pack和non pack,因为您将迭代sizeof。