考虑下面的代码,在一个字节中交换半字节:
#include <stdio.h>
unsigned char swapNibbles(unsigned char x)
{
return ( (x & 0x0F)<<4 | (x & 0xF0)>>4 );
}
int main()
{
unsigned char x = 100;
printf("%u", swapNibbles(x));
return 0;
}
为什么需要使用0x0F和0xF0?相反,我可以写
return ((x<<4) | (x>>4));
我已经开始搜索它了,人们似乎说它不能用负数运算,因为它会用右移的数字填充数字然而用0xF0并不会使数字为正数? 我错过了什么?
答案 0 :(得分:0)
没有anding,你的例子就完全没问题。
如果swapNibbles
有signed char
参数,那么人们可能会认为anding是用来修复表达式的。但是,让我们分析表达式:
(x&0x0f)<<4
:如果x为负数,则x&0x0f
表达式变为非负值,结果符合int
/ unsigned int
(x
的类型1}}升级),所以可以执行转移(x&0xf0)>>4
:如果x为负数,那么x&0xf0
也是负数,因此转换是实现定义的。例如,在x86上,这将复制符号位 - &gt;不是我们想要的。在这种情况下,更好的表达式是(x>>4)&0xf
(仍然是实现定义,但适用于x86)因此,此表达式具有signed char
的实现定义行为,这种行为不是我们想要的x86。
最好将signed char
转换为unsigned char
,并对无符号值执行shift。
注意:我假设signed char
存储在双补码中,而char
是8位(这在问题中暗示,因为我们谈论的是4位半字节这里)。
答案 1 :(得分:0)
两个变体是100%等效的。 gcc和clang都能够将每个代码段转换为简单的rol
指令,并将它们编译为相同的汇编代码(当然可以进行优化)
unsigned char swapNibbles(unsigned char x)
{
return (x & 0x0F)<<4 | (x & 0xF0)>>4;
}
unsigned char swapNibbles2(unsigned char x)
{
return x << 4 | x >>4 ;
}
swapNibbles(unsigned char): # @swapNibbles(unsigned char)
mov eax, edi
rol al, 4
ret
swapNibbles2(unsigned char): # @swapNibbles2(unsigned char)
mov eax, edi
rol al, 4
ret