我的程序通过网络接收消息.这些消息被一些中间件(即其他人无法更改的代码)反序列化.我的程序接收到如下所示的对象:
struct Message {
int msg_type;
std::vector<uint8_t> payload;
};
Run Code Online (Sandbox Code Playgroud)
通过检查,msg_type
我可以确定消息有效负载实际上是,例如,uint16_t
值数组.我想在没有不必要的副本的情况下读取该数组.
我的第一个想法是这样做:
const uint16_t* a = reinterpret_cast<uint16_t*>(msg.payload.data());
Run Code Online (Sandbox Code Playgroud)
但是阅读a
似乎违反了标准.这是第3.10.10条:
如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:
- 对象的动态类型,
- 一个cv限定版本的动态类型的对象,
- 与对象的动态类型类似的类型(如4.4中所定义),
- 与对象的动态类型对应的有符号或无符号类型的类型,
- 一种类型,是有符号或无符号类型,对应于对象动态类型的cv限定版本,
- 聚合或联合类型,包括其元素或非静态数据成员中的上述类型之一(递归地,包括子聚合或包含联合的元素或非静态数据成员),
- 一个类型,它是对象动态类型的(可能是cv限定的)基类类型,
- a
char
或unsigned char
类型.
在这种情况下,a
将是glvalue并且uint16_t*
似乎不符合任何列出的标准.
那么如何在uint16_t
不调用未定义的行为或执行不必要的副本的情况下将有效负载视为值数组?
M.M*_*M.M 15
如果您要逐个使用这些值,那么您可以memcpy
使用uint16_t
或写入payload[0] + 0x100 * payload[1]
等,以了解您想要的行为.这不会"效率低下".
如果你必须调用只接受一个数组的函数uint16_t
,并且你不能改变传递的结构Message
,那么你就不走运了.在标准C++中,您必须制作副本.
如果您正在使用gcc或clang,则另一个选项是-fno-strict-aliasing
在编译相关代码时进行设置.
Dan*_*ica 15
如果您想严格遵循不带UB的C++标准,并且不使用非标准编译器扩展,您可以尝试:
uint16_t getMessageAt(const Message& msg, size_t i) {
uint16_t tmp;
memcpy(&tmp, msg.payload.data() + 2 * i, 2);
return tmp;
}
Run Code Online (Sandbox Code Playgroud)
编译器优化应该避免memcpy
在生成的机器代码中复制; 请参阅,例如,类型Punning,严格别名和优化.
事实上,复制到返回值,但根据您将使用它做什么,此副本也可以被优化(例如,此值可以加载到寄存器中并仅在那里使用).