我曾经遇到过一段似乎有用的代码,但是我很难过为什么会这么做 .
基本上,有一个char缓冲区,在某个位置,包含一个4字节的int存储为big-endian . 代码将提取整数并将其存储为本机小端 . 这是一个简短的例子:
char test[8] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
char *ptr = test;
int32_t value = 0;
value = ((*ptr) & 0xFF) << 24;
value |= ((*(ptr + 1)) & 0xFF) << 16;
value |= ((*(ptr + 2)) & 0xFF) << 8;
value |= (*(ptr + 3)) & 0xFF;
printf("value: %d\n", value);
Value :66051
上面的代码占用前四个字节,将其存储为小端,并打印结果 . 任何人都可以一步一步解释这是如何工作的?我很困惑为什么((* ptr)&0xFF)<< X不会仅仅为任何X> = 8评估为0 .
6 回答
我认为你误解了转换功能 .
表示使用0xff(字节)屏蔽ptr处的值,然后移位24位(不是字节) . 这是一个24/8字节(3字节)到最高字节的转换 .
我写了下面的代码 . 此代码包含两个函数
swapmem()
和swap64()
.swapmem()
交换任意维度的内存区域的字节 .swap64()
交换64位整数的字节 .在本回复结束时,我指出了一个用字节缓冲区解决问题的想法 .
这里的代码:
编译程序后,我有好奇心看到汇编代码gcc生成 . 我发现函数swap64的生成如下所示 .
这个结果是在具有Intel I3 CPU的PC上使用gcc选项编译代码:-Ofast,或-O3,或-O2或-Os .
您可以使用
swap64()
函数之类的东西来解决您的问题 . 像下面的函数我命名为swap32()
:您可以将其用作:
您可能使用的代码基于以下思想:网络上的数字应以BIG ENDIAN模式发送 .
函数
htonl()
和htons()
在BIG ENDIAN中转换32位整数和16位整数,其中您的系统使用LITTLE ENDIAN,否则它们将数字保留为BIG ENDIAN .这里的代码:
编写此代码是为了在LE机器上从LE转换为BE .
您可以使用相反的函数
ntohl()
和ntohs()
从BE转换为LE,这些函数将LE上的整数从BE转换为LE并且不在BE机器上转换 .如果你想将little endian represantion转换为big endian,你可以使用htonl,htons,ntohl,ntohs . 这些函数在主机和网络字节顺序之间转换值 . Big endian也用于基于arm的平台 . 看到这里:https://linux.die.net/man/3/endian
此代码构造值,一次一个字节 .
首先,它捕获最低字节
然后将其移至最高字节
然后将其分配给先前的0初始化值 .
现在"magic"开始发挥作用了 . 由于
ptr
值被声明为char*
,因此将指针前移一个字符 .在看到他们使用指针数学来更新相对起始地址之后,其余的操作与已经描述的操作相同,只是为了保留部分移位的值,它们将
or
值存入现有的value
变量注意,指针数学是你可以做的事情的原因
但它的代价是可能真的搞乱你的指针地址,大大增加了你的SEGFAULT违规风险 . 有些语言认为这是一个问题,他们删除了做指针数学的能力 . 几乎不能进行指针数学运算的指针通常称为引用 .
理解
((*ptr) & 0xFF) << X
评估的关键点之一是Integer Promotion . 值
(*ptr) & 0xff
在被移位之前被提升为Integer
.