c++ 将 2 uint8_t 合并为一个 uint16_t 不起作用?
c++ combining 2 uint8_t into one uint16_t not working?
所以我有一小段代码需要 2 个 uint8_t,然后彼此相邻放置,然后是 returns 个 uint16_t。重点不是添加 2 个变量,而是将它们并排放置并从中创建一个 uint16_t。
我希望它起作用的方式是,当第一个 uint8_t 为 0,第二个 uint8_t 为 1 时,我希望 uint16_t 也为 1。
但是,这在我的代码中并非如此。
这是我的代码:
uint8_t *bytes = new uint8_t[2];
bytes[0] = 0;
bytes[1] = 1;
uint16_t out = *((uint16_t*)bytes);
应该是把bytesuint8_t指针变成uint16_t指针,然后取值。我希望该值为 1,因为 x86 是小端。然而它 returns 256。
将第一个字节设置为 1,将第二个字节设置为 0 使其按预期工作。但我想知道为什么我需要切换字节以使其工作。
谁能给我解释一下?
谢谢!
如果 p
是指向某个多字节值的指针,则:
- "Little-endian" 表示
p
处的字节是 最不重要的 字节,换句话说,它包含值的位 0-7 .
- "Big-endian" 表示
p
处的字节是 最重要的 字节,对于 16 位值,它是位 8-15 .
由于 Intel 是小端字节序,bytes[0] 包含 uint16_t
值的 0-7 位,bytes[1] 包含 8-15 位。由于您正在尝试设置位 0,因此您需要:
bytes[0] = 1; // Bits 0-7
bytes[1] = 0; // Bits 8-15
在小端系统中,小字节放在最前面。换句话说:低字节放在偏移量 0 上,高字节放在偏移量 1 上(依此类推)。所以这个:
uint8_t* bytes = new uint8_t[2];
bytes[0] = 1;
bytes[1] = 0;
uint16_t out = *((uint16_t*)bytes);
生成您想要的 out
= 1 个结果。
但是,如您所见,这很容易出错,所以一般来说,我建议您不要尝试将内容正确地放入内存中然后四处转换,而是这样做:
uint16_t out = lowByte + (highByte << 8);
无论字节序如何,这都适用于任何机器。
编辑:添加了位移说明。
x << y
表示将 x
y
位置的位向左移动(>>
将它们向右移动)。
如果 X 包含位模式 xxxxxxxx
,并且 Y 包含位模式 yyyyyyyy
,则 (X << 8)
生成模式:xxxxxxxx00000000
,并且 Y + (X << 8)
产生:xxxxxxxxyyyyyyyy
.
(并且 Y + (X<<8) + (Z<<16)
产生 zzzzzzzzxxxxxxxxyyyyyyyy
,等等)
单向左移等同于乘以 2,所以 X << 8
等同于 X * 2^8
= X * 256
。这意味着你也可以这样做:Y + (X*256) + (Z*65536)
,但我认为转变更清晰,更好地表达意图。
再次注意:字节顺序无关紧要。左移 8 位将始终清除低 8 位。
您可以在此处阅读更多内容:https://en.wikipedia.org/wiki/Bitwise_operation。注意算术移位和逻辑移位之间的区别 - 在 C/C++ 中,无符号值使用逻辑移位,有符号值使用算术移位。
该地址没有 uint16_t
或兼容对象,因此 *((uint16_t*)bytes)
的行为未定义。
I expect that value to be 1 since x86 is little endian. However it returns 256.
即使程序被修复为具有明确定义的行为,您的期望也是倒退的。在小端中,最低有效字节存储在最低地址中。因此 2 字节值 1 存储为 1, 0 而不是 0, 1.
Does endianess also affect the order of the bit's in the byte or not?
没有办法通过"address"1访问位,所以没有字节顺序的概念。转换为文本时,位通常显示在左侧,最不重要;就像十进制数字的数字一样。我不知道这是否适用于从右到左的书写系统。
1 您可以使用位域为位创建 "virtual addresses"。位域的顺序,即第一个位域是最重要的还是最不重要的是实现定义的,根本不需要与字节字节顺序相关。
这里是将两个八位字节设置为 uint16_t
的正确方法。结果将取决于系统的字节顺序:
// no need to complicate a simple example with dynamic allocation
uint16_t out;
// note that there is an exception in language rules that
// allows accessing any object through narrow (unsigned) char
// or std::byte pointers; thus following is well defined
std::byte* data = reinterpret_cast<std::byte*>(&out);
data[0] = 1;
data[1] = 0;
请注意,假设输入采用本机字节顺序通常不是一个好的选择,尤其是在需要跨多个系统的兼容性时,例如通过网络通信或访问可能与其他系统共享的文件时。
在这些情况下,通信协议或文件格式通常会指定数据采用特定的字节顺序,这可能与目标系统的本机字节顺序相同,也可能不同。网络通信的事实标准是使用big endian。特定字节序的数据可以使用位移位转换为本机字节序,例如 Frodyne 的回答中所示。
您的代码有效,但您误解了如何阅读 "bytes"
#include <cstdint>
#include <cstddef>
#include <iostream>
int main()
{
uint8_t *in = new uint8_t[2];
in[0] = 3;
in[1] = 1;
uint16_t out = *((uint16_t*)in);
std::cout << "out: " << out << "\n in: " << in[1]*256 + in[0]<< std::endl;
return 0;
}
顺便说一句,用这种方式投射时你应该注意对齐。
用数字思考的一种方法是使用 MSB 和 LSB 顺序
MSB 是最高位,LSB 是最低位
小端机。
例如
(u)int32: MSB:Bit 31 ... LSB: Bit 0
(u)int16: MSB:Bit 15 ... LSB: Bit 0
(u)int8 : MSB:Bit 7 ... LSB: Bit 0
with your cast to a 16Bit value the Bytes will arrange like this
16Bit <= 8Bit 8Bit
MSB ... LSB BYTE[1] BYTE[0]
Bit15 Bit0 Bit7 .. 0 Bit7 .. 0
0000 0001 0000 0000 0000 0001 0000 0000
which is 256 -> correct value.
所以我有一小段代码需要 2 个 uint8_t,然后彼此相邻放置,然后是 returns 个 uint16_t。重点不是添加 2 个变量,而是将它们并排放置并从中创建一个 uint16_t。 我希望它起作用的方式是,当第一个 uint8_t 为 0,第二个 uint8_t 为 1 时,我希望 uint16_t 也为 1。 但是,这在我的代码中并非如此。 这是我的代码:
uint8_t *bytes = new uint8_t[2];
bytes[0] = 0;
bytes[1] = 1;
uint16_t out = *((uint16_t*)bytes);
应该是把bytesuint8_t指针变成uint16_t指针,然后取值。我希望该值为 1,因为 x86 是小端。然而它 returns 256。 将第一个字节设置为 1,将第二个字节设置为 0 使其按预期工作。但我想知道为什么我需要切换字节以使其工作。
谁能给我解释一下?
谢谢!
如果 p
是指向某个多字节值的指针,则:
- "Little-endian" 表示
p
处的字节是 最不重要的 字节,换句话说,它包含值的位 0-7 . - "Big-endian" 表示
p
处的字节是 最重要的 字节,对于 16 位值,它是位 8-15 .
由于 Intel 是小端字节序,bytes[0] 包含 uint16_t
值的 0-7 位,bytes[1] 包含 8-15 位。由于您正在尝试设置位 0,因此您需要:
bytes[0] = 1; // Bits 0-7
bytes[1] = 0; // Bits 8-15
在小端系统中,小字节放在最前面。换句话说:低字节放在偏移量 0 上,高字节放在偏移量 1 上(依此类推)。所以这个:
uint8_t* bytes = new uint8_t[2];
bytes[0] = 1;
bytes[1] = 0;
uint16_t out = *((uint16_t*)bytes);
生成您想要的 out
= 1 个结果。
但是,如您所见,这很容易出错,所以一般来说,我建议您不要尝试将内容正确地放入内存中然后四处转换,而是这样做:
uint16_t out = lowByte + (highByte << 8);
无论字节序如何,这都适用于任何机器。
编辑:添加了位移说明。
x << y
表示将 x
y
位置的位向左移动(>>
将它们向右移动)。
如果 X 包含位模式 xxxxxxxx
,并且 Y 包含位模式 yyyyyyyy
,则 (X << 8)
生成模式:xxxxxxxx00000000
,并且 Y + (X << 8)
产生:xxxxxxxxyyyyyyyy
.
(并且 Y + (X<<8) + (Z<<16)
产生 zzzzzzzzxxxxxxxxyyyyyyyy
,等等)
单向左移等同于乘以 2,所以 X << 8
等同于 X * 2^8
= X * 256
。这意味着你也可以这样做:Y + (X*256) + (Z*65536)
,但我认为转变更清晰,更好地表达意图。
再次注意:字节顺序无关紧要。左移 8 位将始终清除低 8 位。
您可以在此处阅读更多内容:https://en.wikipedia.org/wiki/Bitwise_operation。注意算术移位和逻辑移位之间的区别 - 在 C/C++ 中,无符号值使用逻辑移位,有符号值使用算术移位。
该地址没有 uint16_t
或兼容对象,因此 *((uint16_t*)bytes)
的行为未定义。
I expect that value to be 1 since x86 is little endian. However it returns 256.
即使程序被修复为具有明确定义的行为,您的期望也是倒退的。在小端中,最低有效字节存储在最低地址中。因此 2 字节值 1 存储为 1, 0 而不是 0, 1.
Does endianess also affect the order of the bit's in the byte or not?
没有办法通过"address"1访问位,所以没有字节顺序的概念。转换为文本时,位通常显示在左侧,最不重要;就像十进制数字的数字一样。我不知道这是否适用于从右到左的书写系统。
1 您可以使用位域为位创建 "virtual addresses"。位域的顺序,即第一个位域是最重要的还是最不重要的是实现定义的,根本不需要与字节字节顺序相关。
这里是将两个八位字节设置为 uint16_t
的正确方法。结果将取决于系统的字节顺序:
// no need to complicate a simple example with dynamic allocation
uint16_t out;
// note that there is an exception in language rules that
// allows accessing any object through narrow (unsigned) char
// or std::byte pointers; thus following is well defined
std::byte* data = reinterpret_cast<std::byte*>(&out);
data[0] = 1;
data[1] = 0;
请注意,假设输入采用本机字节顺序通常不是一个好的选择,尤其是在需要跨多个系统的兼容性时,例如通过网络通信或访问可能与其他系统共享的文件时。
在这些情况下,通信协议或文件格式通常会指定数据采用特定的字节顺序,这可能与目标系统的本机字节顺序相同,也可能不同。网络通信的事实标准是使用big endian。特定字节序的数据可以使用位移位转换为本机字节序,例如 Frodyne 的回答中所示。
您的代码有效,但您误解了如何阅读 "bytes"
#include <cstdint>
#include <cstddef>
#include <iostream>
int main()
{
uint8_t *in = new uint8_t[2];
in[0] = 3;
in[1] = 1;
uint16_t out = *((uint16_t*)in);
std::cout << "out: " << out << "\n in: " << in[1]*256 + in[0]<< std::endl;
return 0;
}
顺便说一句,用这种方式投射时你应该注意对齐。
用数字思考的一种方法是使用 MSB 和 LSB 顺序
MSB 是最高位,LSB 是最低位
小端机。
例如
(u)int32: MSB:Bit 31 ... LSB: Bit 0
(u)int16: MSB:Bit 15 ... LSB: Bit 0
(u)int8 : MSB:Bit 7 ... LSB: Bit 0
with your cast to a 16Bit value the Bytes will arrange like this
16Bit <= 8Bit 8Bit
MSB ... LSB BYTE[1] BYTE[0]
Bit15 Bit0 Bit7 .. 0 Bit7 .. 0
0000 0001 0000 0000 0000 0001 0000 0000
which is 256 -> correct value.