交换一个字节中的半字节

时间:2017-11-04 19:33:31

标签: c++ bit-manipulation

考虑下面的代码,在一个字节中交换半字节:

#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并不会使数字为正数? 我错过了什么?

2 个答案:

答案 0 :(得分:0)

没有anding,你的例子就完全没问题。

如果swapNibblessigned char参数,那么人们可能会认为anding是用来修复表达式的。但是,让我们分析表达式:

  • (x&0x0f)<<4:如果x为负数,则x&0x0f表达式变为非负值,结果符合int / unsigned intx的类型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