我正在通过网络套接字以大端格式从另一台主机接收数据。如何解释以本机字节序格式接收的字节(例如获取视图或重新解释这些字节)而不复制到临时变量中。
#include <iostream>
#include <cstdint>
struct A {
uint16_t msg_id;
// contains many other fields where total size is greater than 4096
void print() const {
// print all the fields of struct A
}
};
struct B {
uint16_t msg_id;
// contains many other fields where total size is greater than 4096
void print() const {
// print all the fields of struct B
}
};
struct C {
uint16_t msg_id;
// contains many other fields where total size is greater than 4096
void print() const {
// print all the fields of struct C
}
};
int main() {
char buff[8192];
while (true) {
// data is received in network byte order (big endian) but my system is little endian
const auto recvd_len = recvfrom(sock_fd, buff, sizeof(buff), 0, nullptr, nullptr);
const uint16_t msg_id = (buff[0] << 8) | (buff[1] & 0xFF);
switch (msg_id) {
case 0x0001: {
// reinterpret the bytes received as struct A, copy elision
const A* a_obj = reinterpret_cast<const A*>(buff);
a_obj->print();
// the above print call works correctly only if my system is big endian but not little endian
}
break;
case 0x0002: {
// reinterpret the bytes received as struct B, copy elision
const B* b_obj = reinterpret_cast<const B*>(buff);
b_obj->print();
// the above print call works correctly only if my system is big endian but not little endian
}
break;
case 0x0003: {
// reinterpret the bytes received as struct C, copy elision
const C* c_obj = reinterpret_cast<const C*>(buff);
c_obj->print();
// the above print call works correctly only if my system is big endian but not little endian
}
break;
default:
break;
}
}
}
在 C++ 中,这始终是未定义的行为。这是严格的别名违规,因为
buff
(在数组到指针转换之后)是指向对象的指针char
,但您正在print
通过类型访问其成员A
。这是不允许的;请参阅[expr.ref] p9。buff
也可能未对齐,您需要使用它来确保内部对象alignas(A)
的正确对齐。A
C++23 添加了一个非常适合此用例的函数
std::start_lifetime_as
::为此,
A
需要是一个隐式生命周期类 type,这在您的情况下看起来是可能的。std::start_lifetime_as
不幸的是,在撰写本文时还没有编译器实现,因此您需要一个技术上 UB 的解决方法,但显然不是这样:使用
std::launder
,您可以将指向对象的指针转换为指向同一地址的对象char
的指针。A
如果那里实际上没有A
对象,这仍然是未定义的行为,但您可以假设在那里recvfrom
放置了一个对象A
。至少,编译器不能证明recvfrom
没有把 an 放在A
那里,所以它会做你想做的事。关于字节顺序转换的注意事项
请记住,所有这些方法都假设给定对象已经采用本机字节顺序,而不是网络字节顺序。这里没有执行字节序转换。
为了获得数据成员有意义的值,您需要在重新解释后更正它们的字节顺序(无论哪种方法),例如通过使用
std::byteswap
每个成员。