将 uint8_t 数组转换为 C 中的 uint16_t 值
Posted
技术标签:
【中文标题】将 uint8_t 数组转换为 C 中的 uint16_t 值【英文标题】:Casting uint8_t array into uint16_t value in C 【发布时间】:2016-04-27 17:07:17 【问题描述】:我正在尝试将 2 字节数组转换为单个 16 位值。出于某种原因,当我将数组转换为 16 位指针然后取消引用它时,值的字节顺序会被交换。
例如,
#include <stdint.h>
#include <stdio.h>
main()
uint8_t a[2] = 0x15, 0xaa;
uint16_t b = *(uint16_t*)a;
printf("%x\n", (unsigned int)b);
return 0;
打印aa15
而不是15aa
(这是我所期望的)。
这是什么原因,有没有简单的解决方法?
我知道我可以执行 uint16_t b = a[0] << 8 | a[1];
之类的操作(效果很好),但我觉得这个问题应该可以通过强制转换轻松解决,我不确定是什么导致了这里的问题。
【问题讨论】:
这是由于endianness。你不会通过强制转换来解决它:你的 shift-and-add (or) 是好的。 ***.com/questions/22030657/… 【参考方案1】:如 cmets 中所述,这是由于 endianness。
您的机器是 little-endian,这(除其他外)意味着多字节整数值的最低有效字节在前。
如果您在大端机器(例如 Sun)上编译并运行此代码,您将获得预期的结果。
由于您的数组设置为大端,这也恰好是网络字节顺序,您可以使用ntohs
和htons
来解决这个问题。这些函数将 16 位值从网络字节顺序(大端)转换为主机的字节顺序,反之亦然:
uint16_t b = ntohs(*(uint16_t*)a);
有类似的函数称为 ntohl
和 htonl
,它们适用于 32 位值。
【讨论】:
【参考方案2】:这是因为你的机器的字节序。
为了使您的代码独立于机器,请考虑以下功能:
#define LITTLE_ENDIAN 0
#define BIG_ENDIAN 1
int endian()
int i = 1;
char *p = (char *)&i;
if (p[0] == 1)
return LITTLE_ENDIAN;
else
return BIG_ENDIAN;
因此,对于每种情况,您都可以选择应用哪种操作。
【讨论】:
【参考方案3】:由于strict aliasing rule,您无法执行*(uint16_t*)a
之类的操作。即使代码现在看起来可以工作,但它可能会在稍后在不同的编译器版本中中断。
代码的正确版本可能是:
b = ((uint16_t)a[0] << CHAR_BIT) + a[1];
您的问题中建议的涉及a[0] << 8
的版本不正确,因为在具有16 位int
的系统上,这可能会导致有符号整数溢出:a[0]
提升为int
,<< 8
表示@987654329 @。
【讨论】:
【参考方案4】:这可能有助于可视化事物。创建数组时,您有两个字节。当你打印它时,你会得到人类可读的十六进制值,这与它存储的小端方式相反。 1
的值在 little endian 中作为 uint16_t 类型存储如下,其中 a0 是比 a1 低的地址...
a0 a1
|10000000|00000000
注意,最低有效字节在前,但是当我们以十六进制打印值时,最低有效字节出现在右侧,这是我们通常在任何机器上所期望的。
这个程序从最低有效字节开始以二进制打印小端和大端1
...
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
void print_bin(uint64_t num, size_t bytes)
int i = 0;
for(i = bytes * 8; i > 0; i--)
(i % 8 == 0) ? printf("|") : 1;
(num & 1) ? printf("1") : printf("0");
num >>= 1;
printf("\n");
int main(void)
uint8_t a[2] = 0x15, 0xaa;
uint16_t b = *(uint16_t*)a;
uint16_t le = 1;
uint16_t be = htons(le);
printf("Little Endian 1\n");
print_bin(le, 2);
printf("Big Endian 1 on little endian machine\n");
print_bin(be, 2);
printf("0xaa15 as little endian\n");
print_bin(b, 2);
return 0;
这是输出(这是最低有效字节)
Little Endian 1
|10000000|00000000
Big Endian 1 on little endian machine
|00000000|10000000
0xaa15 as little endian
|10101000|01010101
【讨论】:
uint64_t
与问题或 2 字节数组有什么关系?这似乎使一个简单的问题过于复杂。
对不起,我的 DV,因为 1
从未存储为 10000000
我不明白。我没有说它存储为100000001
我用两个字节来证明它是存储的,第一个字节出现在左侧。
左边二进制10000000
的第一个字节是十进制128
。你一心想进一步混淆 OP。
无论字节字节序如何,没有人将位写入小端序。以上是关于将 uint8_t 数组转换为 C 中的 uint16_t 值的主要内容,如果未能解决你的问题,请参考以下文章
将 unsigned char 数组转换为 uint8_t 数组?