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

C++ C++;将结构强制转换为std::vector<;char>;内存对齐,c++,vector,struct,casting,reinterpret-cast,C++,Vector,Struct,Casting,Reinterpret Cast,我试图将一个结构转换成一个字符向量。 我想在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

我试图将一个结构转换成一个字符向量。 我想在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]);
auto const ptr=reinterpret\u cast(&in);
std::向量缓冲区(ptr,ptr+sizeof-in);
//通过udp发送和接收
输入*my_struct=reinterpret_cast(&buffer[0]);
问题是: 除了我的uint8\u t或int8\u t外,其他都可以正常工作。 我不知道为什么,但无论何时何地,我在结构中放置了一个1字节的值, 当我将其回溯时,该值是不可读的(但其他值是可读的) 我试着只输入16位的值,即使使用 最大值,以便所有位都正常

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

多谢各位

我试图将一个结构转换成一个字符向量

不能将任意对象强制转换为向量。您可以将对象强制转换为
char
数组,然后将该数组复制到向量中(这实际上就是代码所做的)

auto const ptr=reinterpret\u cast(&in);
std::向量缓冲区(ptr,ptr+sizeof-in);
第二行定义了一个新的向量,并通过将表示对象的字节复制到其中来初始化它。这是合理的,但与你所说的你试图做的不同

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

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

在x86上,您可以使用未对齐的数据,但无论何时访问未对齐的成员变量,都可能会付出巨大的性能代价。如果packing设置为1,并且第一个字段的大小为奇数,则可以保证下一个字段不会对齐。我劝你重新考虑一下。设计结构,使所有字段都位于其自然对齐边界处,并且无需调整填充。这可能会使您的结构更大一些,但它将避免所有的对齐和性能问题

如果要在wire格式中省略填充字节,则必须将相关字段逐字节复制到wire格式中,然后将其复制回接收端

关于下列事项的旁白:

#定义PACK(_声明__)_pragma(PACK(push,1))_声明__pragma(PACK(pop))

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

1 vector类具有动态分配的内存,并在其中使用指针。因此,您不能发送向量(但可以发送底层数组)

2. SFML有一个很好的类来完成这项工作,称为sf::packet。它是免费的、开源的、跨平台的

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

3. memcpy是你最好的朋友。它被设计成便携的,你可以利用它为你的优势

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

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

如果您能够做出我提到的保证,那么在LINUX上发送就非常容易了

// POSIX
void send(int fd, Inputs& input)
{
    int error = sendto(fd, &input, sizeof(input), ..., ..., ...);
    ...
}
winsock2使用char*而不是void*:(


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

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

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

步骤1:删除所有宏和强制转换。步骤2:使用序列化库,例如。步骤3:利润。由于许多原因,您想要做的是错误的。没有任何东西可以保证另一方将获得相同的结构,除非它们都是使用完全相同的编译器(版本也相同)并在完全相同的平台和相同的体系结构中运行。有没有一种方法可以序列化,而不使用外部库?这是一个项目,我对库有很多限制。逐个成员序列化,逐字节序列化。当实体序列化的限制被双方理解并解决时,实体序列化没有什么错当然比任何“适当的”序列化都快。不过,我不清楚这个问题。当我将2个字节放入uint8_t时,这是什么意思?
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++;
}