反转数字的位

reverse a number's bits

本文关键字:数字      更新时间:2023-10-16

这是一个C++类,用于尊重LeetCode讨论中的位。 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;
    }
};

考虑内存上提供不同的布局模具。

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

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

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

指针被分配到与uint32_t变量相同的内存位置。

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

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

因此,在列表中:

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

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

该解决方案使用蛮力来还原位。它声明了一个位域结构(即成员后跟 :1 ),具有 32 位字段,每个字段一个位。然后,通过将输入的地址转换为指向该结构的指针,将 32 位输入视为此类结构。 然后c被声明为该类型的变量,该变量通过恢复位的顺序进行初始化。最后,c表示的位域被重新解释为整数,您就完成了。

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

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

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

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

首先,发布的代码有一个小错误。该行

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. 将其视为类型 bs*(bs*)&n ) 的指针
  3. 将指针分配给变量。( bs *b =

通过这样做,我们能够选择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

这将返回一个uint8_t其值与输入参数按位反转。

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

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

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

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

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