C旋转位域(需要说明)

时间:2016-09-12 02:32:34

标签: c rotation bit-manipulation

我想向左旋转1位字节。我正在查看此站点的几个示例,并遇到了此代码。虽然它有效,但我一步一步地欣赏 它是如何工作的。

unsigned int _rotl(const unsigned int value, int shift) 
{
    if ((shift &= sizeof(value)*8 - 1) == 0)           //What does this do?
      return value;
    return (value << shift) | (value >> (sizeof(value)*8 - shift));
}

首先,第一部分的作用是什么?而且最后一部分不会那么多,你几乎要删掉一些比特?

例如:

value= 0x50    //01010000
shift = 4

我会 为了

value << shift

01010000 << 4  =>   00000000

而且     值&gt;&gt; (sizeof(value)* 8 - shift)

01010000>> (4*8 - 4)   => 00000000

因此,对两者进行OR运算都会给我0.我的理解显然是错误的,但是我很感激任何人,不管怎么说。这对于像我这样的乞丐来说。感谢。

2 个答案:

答案 0 :(得分:2)

让我们一步一步地采取行动:

unsigned int _rotl(const unsigned int value, int shift) 
{
  if ((shift &= sizeof(value)*8 - 1) == 0)           //What does this do?
    return value;
  return (value << shift) | (value >> (sizeof(value)*8 - shift));
}

第一行:

if ((shift &= sizeof(value)*8 - 1) == 0)           //What does this do?

此声明是一个优化和检查汇总到一行。如果满足以下两个条件之一,则返回TRUE:

  1. Shift为零(即不要旋转任何位)
  2. shift指定的轮播次数无效
  3. 该语句否则返回FALSE,但也计算实现所需结果所需的最小旋转次数,并将该值存储在shift中。换句话说,它会计算shift = shift % size_of_integer_data_type

    例如,如果您有一个32位整数,则将其旋转32位不会产生任何影响。如果将它旋转64,96或32的任何其他倍数,也不会完成任何操作。如果我们轮换的效果什么也没做,那么我们就会节省很多时间并且提前退出。

    但是,我们可能还指定了比必要更多的工作。如果你有一个32位整数,那么将它旋转一位与旋转33位或65位或97位等效果相同。这段代码识别这个事实,所以如果你指定shift为97 ,它重新分配shift=1并减少无关的轮换。

    语句sizeof(value)*8 - 1返回的值小于value表示中的位数。例如,如果sizeof(value)的计算结果为4(它将在具有32位整数的系统上计算),则为4*8 - 1 = 31

    &=运算符是按位AND,具有赋值。这意味着我们在shiftsizeof(value)*8 - 1之间执行按位AND,并将结果分配给shift。和以前一样,该表达式的右侧等于value中的位数减去1。因此,这具有掩蔽shift的所有比特的效果,其大于value的表示的大小,其反过来具有计算shift = shift % size_of_integer_data_type的效果。

    具体而言,重新考虑32位案例。和以前一样,sizeof(value)*8-1的计算结果为31.按位,此值为0000 0000 0000 0000 0000 0000 0001 1111。该值与shift按位进行AND运算。 shift的第6至第32位的任何位都设置为零,而第1至第5位的位不变。如果你要指定97个旋转,结果将是一个。

      0000 0000 0000 0000 0000 0000 0110 0001 (97)
    & 0000 0000 0000 0000 0000 0000 0001 1111 (31)
    =========================================
      0000 0000 0000 0000 0000 0000 0000 0001 (1)
    

    这里要做的最后一件事是回忆一下,在C中,赋值语句的返回值是赋值的值。因此,如果shift的新值为零,那么我们立即返回,否则我们继续。

    第二行:

    return (value << shift) | (value >> (sizeof(value)*8 - shift));
    

    由于C没有旋转运算符(它只有左右移位),我们必须分别计算低阶位和高阶位,然后将它们与按位OR组合。这条线是分别计算每一面的简单问题。

    语句value << shift计算高位。它将位模式向左移动shift个位置。另一个语句通过将位模式向右移位size_of_integer_type - shift位来计算低位。这在示例中很容易看到。

    假设value的小数值为65535,而shift的值为26.那么起始值为:

      0000 0000 0000 0000 1111 1111 1111 1111 (65535)
    

    左移给我们:

      1111 1100 0000 0000 0000 0000 0000 0000 (65535 << 26)
    

    正确的转变给了我们:

      0000 0000 0000 0000 0000 0011 1111 1111 (65535 >> 6)
    

    然后按位或组合这些结果:

      1111 1100 0000 0000 0000 0000 0000 0000 (65535 << 26)
    | 0000 0000 0000 0000 0000 0011 1111 1111 (65535 >> 6)
    =========================================
      1111 1100 0000 0000 0000 0011 1111 1111 (65535 rot 26)
    

    您可以重新编写此代码并获得相同的正确结果:

    unsigned int _rotl(const unsigned int value, int shift)
    {
        //Assume 8 bits in a byte
        unsigned bits_in_integer_type = sizeof(value)*8;
        shift = shift % bits_in_integer_type;
    
        if( shift == 0 ) return value; //rotation does nothing
    
        unsigned high_bits = value << shift;
        unsigned low_bits = value >> (bits_in_integer_type - shift);
    
        return high_bits | low_bits;
    }
    

答案 1 :(得分:1)

unsigned int _rotl(const unsigned int value, int shift) 
{
    // If all bits in value are zero, do nothing and return
    int bitmaskOfAllOnes = sizeof(value)*8 - 1;
    if ((shift &= bitmaskOfAllOnes) == 0)           //What does this do?
      return value;

    // Shift value to the left
    (value << shift) 
    // shifting right to handle the wrap around
    | (value >> (sizeof(value)*8 - shift));
}

e.g. using 16-bit ints 
value = 0x1001
shift = 4
sizeof(value) => 2
//sizeof(value)*8 - 1 => 0xffff
sizeof(value)*8 - 1 => 15 Decimal
shift &= bitmaskOfAllOnes => 0x1001 (i.e. not zero)
value << shift => 0x0010
sizeof(value)*8 - shift => 12
value >> (sizeof(value)*8 - shift) => 0x001
0x0010 | 0x001 => 0x0011