将 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] &lt;&lt; 8 | a[1]; 之类的操作(效果很好),但我觉得这个问题应该可以通过强制转换轻松解决,我不确定是什么导致了这里的问题。

【问题讨论】:

这是由于endianness。你不会通过强制转换来解决它:你的 shift-and-add (or) 是好的。 ***.com/questions/22030657/… 【参考方案1】:

如 cmets 中所述,这是由于 endianness。

您的机器是 little-endian,这(除其他外)意味着多字节整数值的最低有效字节在前。

如果您在大端机器(例如 Sun)上编译并运行此代码,您将获得预期的结果。

由于您的数组设置为大端,这也恰好是网络字节顺序,您可以使用ntohshtons 来解决这个问题。这些函数将 16 位值从网络字节顺序(大端)转换为主机的字节顺序,反之亦然:

uint16_t b = ntohs(*(uint16_t*)a);

有类似的函数称为 ntohlhtonl,它们适用于 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] &lt;&lt; 8 的版本不正确,因为在具有16 位int 的系统上,这可能会导致有符号整数溢出:a[0] 提升为int&lt;&lt; 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 数组?

如何将字符数组转换为 uint8_t

将 Intrinsic xmm 寄存器转换为 uint8_t 数组[16]

如何将 uint8_t 数组与 C 中的字符串进行比较?

使用舍入从浮点数转换为 uint8_t

c char 数组到 uint8_t 数组