我正在尝试运行此代码:
#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
}
输出:
255
我认为应该输出:
65280
因为0xff
代表新MSBs
的{{1}}而word
代表新0x00
的{{1}}。我错过了什么?
答案 0 :(得分:3)
您的演员表的行为未定义:这是因为类型不相关。
如果您想将两个uint8_t
合并为一个uint16_t
,请创建一个包含2个元素的前者的数组,并将memcpy
加入{ {1}}。
(不要考虑uint16_t
union
和uint16_t
数组作为回读非联盟成员的行为你用来设置联合数据的那个也是 undefined 。)
答案 1 :(得分:2)
您违反了严格的别名规则。你不能这样做。至于小端英特尔CPU,这是你最不担心的事情。
答案 2 :(得分:1)
正如已经提到的其他答案和评论:为整数表示构建指针是未定义的行为,但您目击的是与主机endianess有关,主机endianess是主机如何解释一系列字节以形成更长的单词。
从字节缓冲区(在这种情况下为std::array<std::uint8_t, 2>
)到实际数据被称为deserialization,并且最简单的方法来执行此主机endianess不知道(假设缓冲区是大端)是移位字节融入积分。有关浮动的可移植序列化,请参阅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);
答案 3 :(得分:0)
要进行类似的转换,请通过联合键入-pin,以避免破坏严格的别名优化。见strict aliasing and type punning
重要的一句是:&#39;严格地说,读取与写入的联盟不同的联盟成员在ANSI / ISO C99中是未定义的,除了在类型 - 惩罚到char *的特殊情况下,类似以下示例:转换为char *。然而,这是一个非常普遍的习惯用语,并得到所有主要编译器的良好支持。实际上,以任何顺序阅读和写作任何工会成员都是可以接受的做法。&#39;
那些谈论UB的人可以坐在他们的象牙塔里,为我所关心的一切,阅读我联系的信息并学习一些东西。