反转一个数字的位

时间:2015-06-12 22:18:35

标签: c++ bit-manipulation

这是一个用于从LeetCode讨论revering位的C ++类。 https://leetcode.com/discuss/29324/c-solution-9ms-without-loop-without-calculation 例如,给定输入43261596(以二进制表示为00000010100101000001111010011100),返回964176192(以二进制表示为00111001011110000010100101000000)。

有没有人可以解释一下?非常感谢你!

class Solution {
public:
    uint32_t reverseBits(uint32_t n) {
        struct bs
        {
            unsigned int _00:1; unsigned int _01:1; unsigned int _02:1; unsigned int _03:1;
            unsigned int _04:1; unsigned int _05:1; unsigned int _06:1; unsigned int _07:1;
            unsigned int _08:1; unsigned int _09:1; unsigned int _10:1; unsigned int _11:1;
            unsigned int _12:1; unsigned int _13:1; unsigned int _14:1; unsigned int _15:1;
            unsigned int _16:1; unsigned int _17:1; unsigned int _18:1; unsigned int _19:1;
            unsigned int _20:1; unsigned int _21:1; unsigned int _22:1; unsigned int _23:1;
            unsigned int _24:1; unsigned int _25:1; unsigned int _26:1; unsigned int _27:1;
            unsigned int _28:1; unsigned int _29:1; unsigned int _30:1; unsigned int _31:1;
        } *b = (bs*)&n, 
        c = 
        {
              b->_31, b->_30, b->_29, b->_28
            , b->_27, b->_26, b->_25, b->_24
            , b->_23, b->_22, b->_21, b->_20
            , b->_19, b->_18, b->_17, b->_16
            , b->_15, b->_14, b->_13, b->_12
            , b->_11, b->_10, b->_09, b->_08
            , b->_07, b->_06, b->_05, b->_04
            , b->_03, b->_02, b->_01, b->_00
        };

        return *(unsigned int *)&c;
    }
};

5 个答案:

答案 0 :(得分:3)

cast 视为在内存中提供不同的布局模板。

使用此模板图片,代码是无符号整数存储器位置上32位模板的布局。

因此,不是将内存视为uint32_t,而是将内存视为32位。

创建指向32位结构的指针。

指针被指定为与uint32_t变量相同的内存位置。

指针将允许对内存位置进行不同的处理。

创建一个32位(使用该结构)的临时变量。 使用初始化列表初始化变量 初始化列表中的位字段来自原始变量,以相反的顺序列出。

所以,在列表中:

  new bit 0 <-- old bit 31  
  new bit 1 <-- old bit 30  

这种方法的基础依赖于初始化列表。 作者让编译器反转这些位。

答案 1 :(得分:3)

该解决方案使用强力来恢复比特。 它声明了一个位域结构(当成员后跟:1时),每个位为32位,每位一位。 然后通过将输入的地址转换为指向结构的指针,将32位输入视为这种结构。然后c被声明为该类型的变量,通过恢复位的顺序来初始化。 最后,c表示的位域被重新解释为整数,你就完成了。

汇编程序不是很有趣,因为gcc资源管理器显示: https://goo.gl/KYHDY6

答案 2 :(得分:2)

它不会按每次转换进行转换,但它只是以不同的方式查看相同的内存地址。它使用int n的值,但获取指向该地址的指针,对指针进行类型转换,这样,您可以将该数字解释为32个单独位的结构。因此,通过此结构b,您可以访问该数字的各个位。

然后,对于新的结构c,通过将数字的第31位放在输出结构c的第0位,第1位的第30位,等等,来直接设置每个位。

之后,返回结构的内存位置的值。

答案 3 :(得分:2)

首先,发布的代码有一个小bug。这条线

return *(unsigned int *)&c;
如果sizeof(unsigned int)不等于sizeof(uint32_t),则

不会返回准确的数字。

该行应为

return *(uint32_t*)&c;

关于它是如何工作的问题,我将尝试用较小的类型uint8_t解释它。

功能

uint8_t reverseBits(uint8_t n) {
    struct bs
    {
        unsigned int _00:1; unsigned int _01:1; unsigned int _02:1; unsigned int _03:1;
        unsigned int _04:1; unsigned int _05:1; unsigned int _06:1; unsigned int _07:1;
    } *b = (bs*)&n, 
    c = 
    {
          b->_07, b->_06, b->_05, b->_04
        , b->_03, b->_02, b->_01, b->_00
    };

    return *(uint8_t *)&c;
}

使用本地结构。本地结构定义为:

    struct bs
    {
        unsigned int _00:1; unsigned int _01:1; unsigned int _02:1; unsigned int _03:1;
        unsigned int _04:1; unsigned int _05:1; unsigned int _06:1; unsigned int _07:1;
    };

该结构有八个成员。结构的每个成员都是宽度为1的位域。类型为bs的对象所需的空间为8位。

如果将struct的定义与该类型的变量分开,则该函数将为:

uint8_t reverseBits(uint8_t n) {
    struct bs
    {
        unsigned int _00:1; unsigned int _01:1; unsigned int _02:1; unsigned int _03:1;
        unsigned int _04:1; unsigned int _05:1; unsigned int _06:1; unsigned int _07:1;
    };

    bs *b = (bs*)&n;
    bs c =
    {
         b->_07, b->_06, b->_05, b->_04
       , b->_03, b->_02, b->_01, b->_00
    };

    return *(uint8_t *)&c;
}

现在,让我们说这个函数的输入是0xB7,它是二进制的1011 0111。这条线

    bs *b = (bs*)&n;

表示:

  1. n&n
  2. 的地址
  3. 将其视为类型bs*(bs*)&n
  4. 的指针
  5. 将指针指定给变量。 (bs *b =
  6. 通过这样做,我们可以选择n的每一位,并使用b的成员获取其值。在该行的末尾,

    b->_00的值为1
    b->_01的值为0
    b->_02的值为1
    b->_03的值为1
    b->_04的值为0
    b->_05的值为1
    b->_06的值为1
    b->_07的值为1

    声明

        bs c =
        {
             b->_07, b->_06, b->_05, b->_04
           , b->_03, b->_02, b->_01, b->_00
        };
    

    只需创建c,使得c的位与*b的位相反。

    该行

        return *(uint8_t *)&c;
    

    表示:

    1. c。的地址,其值为位模式1110 1101
    2. 将其视为uint8_t*类型的指针。
    3. 取消引用指针并返回结果uint8_t
    4. 返回uint8_t,其值与输入参数按位反转。

答案 4 :(得分:0)

这不是混淆,但是一两条评论会帮助无辜者。关键是在变量声明的中间,第一步是识别这里只有一行“代码”,其他一切都是变量声明和初始化。

在声明和初始化之间,我们发现:

} *b = (bs*)&n,
c =
{

这声明了一个变量'b',它是刚刚定义的结构“bs”的指针(*)。然后它将函数参数'n'的地址(unit_32_t)转换为类型指针到bs,并将其分配给'b',有效地创建uint_32_t的 union 和位数组bs 。 然后声明第二个变量,一个名为“c”的实际struct bs,并通过指针'b'初始化它。 b-&gt; _31初始化c._00,依此类推。 因此,在创建“b”和“c”之后,按顺序,除了返回“c”的值之外没有什么可做的。

代码的作者和编译器知道在结构定义结束后,可以在“;”之前创建该类型的变量或与该类型相关的变量,这就是@Thomas Matthews关闭的原因,“作者让编译器反转这些位。“