将字符缓冲区转换为结构体
Convert char buffer to struct
我有一个char
缓冲区buf
包含buf[0] = 10
, buf[1] = 3
, buf[2] = 3
, buf[3] = 0
, buf[4] = 58
,
和结构:
typedef struct
{
char type;
int version;
int length;
}Header;
我想把buf
转换成Header
。现在我使用函数
int getByte( unsigned char* buf)
{
int number = buf[0];
return number;
}
int getInt(unsigned char* buf)
{
int number = (buf[0]<<8)+buf[1];
return number;
}
main()
{
Header *head = new Header;
int location = 0;
head->type = getByte(&buf[location]);
location++; // location = 1
head->version = getInt(&buf[location]);
location += 2; // location = 3
head->ength = getInt(&buf[location]);
location += 2; // location = 5
}
我正在寻找一个解决方案,如
Header *head = new Header;
memcpy(head, buf, sizeof(head));
在这种情况下,Header
, head->type
中的第一个值是正确的,其余的是垃圾。能否将unsigned char* buf
转换为Header
?
唯一完全可移植且安全的方法是:
void convertToHeader(unsigned char const * const buffer, Header *header)
{
header->type = buffer[0];
header->version = (buffer[1] << 8) | buffer[2];
header->length = (buffer[3] << 8) | buffer[4];
}
和
void convertFromHeader(Header const * const header, unsigned char * buffer)
{
buffer[0] = header->type;
buffer[1] = (static_cast<unsigned int>(header->version) >> 8) & 0xFF;
buffer[2] = header->version & 0xFF;
buffer[3] = (static_cast<unsigned int>(header->length) >> 8) & 0xFF;
buffer[4] = header->length & 0xFF;
}
例子参见将字节数组转换为整数的解释
编辑
对前面链接的快速总结:其他可能的解决方案(例如memcpy
或union
)根据不同系统的端序是不可移植的(您所做的可能是至少两个异构系统之间的通信)=>一些系统byte[0]是int的LSB, byte[1]是MSB,而另一些则相反。
另外,由于对齐,struct Header
可以大于5个字节(如果对齐是2个字节,在您的情况下可能是6个字节!)(参见这里的例子)
最后,根据某些平台的对齐限制和混叠规则,编译器可能会生成错误的代码。
你需要你的version
和length
有相同的长度作为你的buf
数组的2个元素;也就是说,您需要使用在<cstdint>
中定义的uint16_t
类型,而不是可能更长一些的int
类型。而且你还需要让buf
成为uint8_t
的数组,因为char被允许占用超过1个字节!
你可能还需要把type
移到最后;否则编译器几乎肯定会在它之后插入一个填充字节,以便能够将version
对齐到2字节的边界(一旦你使它成为uint16_t
,因此是2字节);然后你的buf[1]
就会停在那里,而不是你想要的地方。顺便说一下,这可能是你现在观察到的:通过char
后跟int
,这可能是4
字节,你有3
字节的填充,元素1
到3
的数组被插入那里(=永远丢失)。
另一个解决方案是修改您的buf
数组更长,并有空填充字节,这样数据将实际上与结构体字段对齐。
值得再次提及的是,正如在注释中指出的那样,sizeof(head)
返回系统上指针的大小,而不是Header
结构体的大小。你可以直接写sizeof(Header)
;但是在这种微观管理层面,如果你只写"5
",你不会失去任何更多的灵活性,真的。
同样,顺序也会给你带来麻烦。处理器没有义务按照您期望的顺序而不是相反的顺序存储数字的字节;毕竟两者都有内在意义。这意味着盲目地将字节buf[0], buf[1]
复制到一个数字中可能会导致(buf[0]<<8)+buf[1]
,但也会导致(buf[1]<<8)+buf[0]
,如果数据类型是4
字节(int
通常是),甚至会导致(buf[1]<<24)+(buf[0]<<16)
。即使它现在在您的计算机上工作,至少有一个地方相同的代码会导致垃圾。除非,也就是说,这些字节实际上来自于首先重新解释一个数字。然而,在这种情况下,代码是错误的(不可移植的)现在。
…值得吗?
考虑到所有的事情,我强烈建议保持你现在处理它们的方式。也许可以简化一下。将字节转换为int然后再转换为字节,或者获取字节的地址再次对其解引用,也不需要没有描述性名称和除了返回之外没有其他目的的辅助变量,或者是一个你事先知道值的变量。
只做
int getTwoBytes(unsigned char* buf)
{
return (buf[0]<<8)+buf[1];
}
main()
{
Header *head = new Header;
head->type = buf[0];
head->version = getTwoBytes(buf + 1);
head->length = getTwoBytes(buf + 3);
}
更好的方法是创建某种序列化/反序列化例程。
同样,我不仅会使用int
或char
类型,还会使用更具体的int32_t
等,这只是平台独立的方式(好吧,实际上你也可以用pragma pack打包你的数据结构)。
struct Header
{
char16_t type;
int32_t version;
int32_t length;
};
struct Tools
{
std::shared_ptr<Header> deserializeHeader(const std::vector<unsigned char> &loadedBuffer)
{
std::shared_ptr<Header> header(new Header);
memcpy(&(*header), &loadedBuffer[0], sizeof(Header));
return header;
}
std::vector<unsigned char> serializeHeader(const Header &header)
{
std::vector<unsigned char> buffer;
buffer.resize(sizeof(Header));
memcpy(&buffer[0], &header, sizeof(Header));
return buffer;
}
}
tools;
Header header = {'B', 5834, 4665};
auto v1 = tools.serializeHeader(header);
auto v2 = tools.deserializeHeader(v1);
- 使用结构体 c++ 进行强制转换
- 使用std::copy将一个结构体的数组转换为另一个结构的Vector
- 如何将c结构体定义转换为c++
- 使用memcpy将结构体转换为字符数组
- 在结构体中包装数字以避免隐式转换的利弊
- 将字符数组类型转换为结构体(不是类/对象)
- 缓冲区到结构体的转换
- 将结构体强制转换为QByteArray类型
- 从结构体到char *的转换问题
- 将malloc转换为新的c++ for结构体
- 将c++中的结构体转换为Ruby
- 多态和通过带指针的结构体进行强制转换
- 如何将结构体强制转换为char[]数组
- int型到自定义结构体的转换
- 将POD结构体强制转换为派生类型
- Boost Fusion:将适应的结构体类型转换为文本
- 将字符缓冲区转换为结构体
- 将c++结构体转换为c#并使用它
- 如何将带有Union的c++结构体转换为c#
- 如何将带数组的结构体转换为类