将uint8_t数组转换为C

时间:2016-04-27 17:07:17

标签: c arrays

我正在尝试将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];之类的东西(它确实可以正常工作),但我觉得这个问题应该可以通过强制转换轻松解决,而且我不确定是什么原因引起了这个问题。

4 个答案:

答案 0 :(得分:6)

正如评论中所提到的,这归因于endianness

你的机器是little-endian,其中(除其他外)意味着多字节整数值首先具有最低有效字节。

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

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

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

有一些名为ntohlhtonl的类似函数可以处理32位值。

答案 1 :(得分:0)

这是因为您机器的结束。

为了让您的代码独立于机器,请考虑以下功能:

#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;
}

因此,对于每种情况,您都可以选择要应用的操作。

答案 2 :(得分:0)

由于strict aliasing rule,您无法执行b = ((uint16_t)a[0] << CHAR_BIT) + a[1]; 之类的任何操作。即使代码现在似乎有用,但它可能会在以后的其他编译器版本中中断。

正确版本的代码可以是:

a[0] << 8

涉及int的问题中建议的版本不正确,因为在具有16位a[0]的系统上,这可能会导致有符号整数溢出:int提升为<< 8* 256表示{{1}}。

答案 3 :(得分:-1)

这可能有助于形象化。创建数组时,按顺序有两个字节。当您打印它时,您将获得人类可读的十六进制值,这与存储它的小端方式相反。作为uint16_t类型的小端的值1存储如下,其中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