我正在尝试将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];
之类的东西(它确实可以正常工作),但我觉得这个问题应该可以通过强制转换轻松解决,而且我不确定是什么原因引起了这个问题。
答案 0 :(得分:6)
正如评论中所提到的,这归因于endianness。
你的机器是little-endian,其中(除其他外)意味着多字节整数值首先具有最低有效字节。
如果您在big-endian机器(例如Sun)上编译并运行此代码,您将获得预期的结果。
由于您的数组设置为big-endian,也恰好是网络字节顺序,因此您可以使用ntohs
和htons
来解决此问题。这些函数将16位值从网络字节顺序(大端)转换为主机的字节顺序,反之亦然:
uint16_t b = ntohs(*(uint16_t*)a);
有一些名为ntohl
和htonl
的类似函数可以处理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