套管指针(unit8_t 到 uint16_t)返回字节的反向表示
Posted
技术标签:
【中文标题】套管指针(unit8_t 到 uint16_t)返回字节的反向表示【英文标题】:Casing pointer (unit8_t to uint16_t) return reversed representation of the bytes 【发布时间】:2016-02-24 14:55:01 【问题描述】:我正在尝试运行此代码:
#include <iostream>
#include <string>
#include <cstdint>
#include <array>
int main()
std::array<std::uint8_t, 2> one_byte_array;
one_byte_array[0] = 0xff;
one_byte_array[1] = 0x00;
auto ptr8 = one_byte_array.data();
std::uint16_t* ptr16 = (std::uint16_t*)ptr8;
std::cout << *ptr16;
return 0
Live Demo
它输出:
255
我认为它应该输出:
65280
因为0xff
代表新word
的MSBs
,而0x00
代表新word
的LSBs
。我错过了什么?
【问题讨论】:
你的机器上运行这段代码的 CPU 是多少?你确定 CPU 使用的是大端序吗? @ErikAlapää,工会也不好:) @ErikAlapää,将其作为答案发布,以便我投反对票;) @ErikAlapää:这就是你对健全编译器的定义。它不是我的。我的定义是执行标准的。 公牛。memcpy
成语是正确的,并不复杂。
【参考方案1】:
你的演员的行为是未定义:这是因为类型是不相关的。
如果您想将两个 uint8_t
合并为一个 uint16_t
,则创建一个具有 2 个元素的前者的 数组,然后将 memcpy
合并到 uint16_t
中。
(不要将union
的uint16_t
和uint8_t
的数组视为读回不是您用来设置的联合成员的行为工会的数据也是未定义。)
【讨论】:
是的。如此接近,却又如此遥远! 谢谢..(std::uint16_t*)ptr8。这是UB。对吗? 是的。编译器保留吃掉你的猫的权利,如果你写了然后试图尊重指针。 (数据在特定平台上可能有不同的对齐要求,这是语言没有定义行为的一个很好的理由)。 @Bathsheba,不完全是。转换本身不能导致吃猫——这只是一个指针转换。通过它的访问 (std::cout << *ptr16
) 正在吃猫的 UB。
@SergeyA:确实你是对的。已修改,但我仍然建议将您的猫放在安全的地方。【参考方案2】:
您违反了严格的别名规则。你不能这样做。至于小端英特尔 CPU,这是您最不必担心的。
【讨论】:
【参考方案3】:正如其他答案和 cmets 已经提到的:将指针转换为整数表示是未定义的行为,但您所见证的与主机字节序有关,即主机如何解释一系列字节以形成更长的单词。
从字节缓冲区(在这种情况下为std::array<std::uint8_t, 2>
)到实际数据称为deserialization,并且不知道主机字节序的最简单方法(假设缓冲区为大字节序)是将字节转换为整数。对于浮点的可移植序列化,请参阅this answer
std::array<std::uint8_t, 2> one_byte_array;
one_byte_array[0] = 0xff;
one_byte_array[1] = 0x00;
uint16_t data = one_byte_array[0] << 8 | one_byte_array[1];
其实this answer已经解释得更好了。
另一种方法是使用ntohs。
std::array<std::uint8_t, 2> one_byte_array;
one_byte_array[0] = 0xff;
one_byte_array[1] = 0x00;
uint16_t data;
std::memcpy(&data, one_byte_array.data(), 2);
data = ntohs(data);
【讨论】:
【参考方案4】:要进行类似的转换,您可以通过联合进行类型双关语,以避免破坏严格的别名优化。见strict aliasing and type punning
重要的引用是这样的:'严格来说,读取与写入对象不同的联合成员在 ANSI/ISO C99 中是未定义的,除非在类型双关语到 char* 的特殊情况下,类似于示例下面:转换为 char*。然而,它是一个非常常见的习惯用法,并且得到所有主要编译器的良好支持。实际上,以任何顺序对工会的任何成员进行读写都是可以接受的做法。'
那些谈论 UB 的人可以坐在他们的象牙塔里,只要我在乎,阅读我链接的信息并学习一些东西。
【讨论】:
Eric,使用不受支持的工具而不是使用受支持的 (memcpy
) 有什么意义?
@SergeyA 对于某些用途,memcpy 更自然。但是双关语联合成语是如此普遍,以至于它必须得到编译器的支持,否则很多现有代码都会中断。 Linus Torvalds 本人曾为此与 gcc 人员争吵过,以及严格的别名优化。
Erik,我不建议在与我讨论时引用 Linus 作为权威。我对 Linux 作为操作系统的评价很低。 )) 无论如何,有什么好处?
@SergeyA AFAIK,联合就是为此而设计的,这是告诉编译器“这些值属于不同类型但可能有别名”的一种清晰方式。此外,AFAIK,通过联合的类型双关语用于每个主要的操作系统内核,以及许多其他关键任务代码。至于 Linux 的质量 - 每次我阅读内核代码时,我都会对有多少优秀的编码人员在内核上工作以及他们设法跟上 Linux 发展的惊人速度印象深刻。但至于 Torvalds,他最有创意和最酷的发明不是 Linux,而是 Git。以上是关于套管指针(unit8_t 到 uint16_t)返回字节的反向表示的主要内容,如果未能解决你的问题,请参考以下文章
从uint32_t [16]数组到uint32_t变量序列的64位副本
SIMD -> uint16_t 数组到浮点数组在浮点上工作,然后返回到 uint16_t
将 uint8_t 数组转换为 C 中的 uint16_t 值