反转数字的位
reverse a number's bits
这是一个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;
说:
- 取
n
地址 (&n
) - 将其视为类型
bs*
((bs*)&n
) 的指针 - 将指针分配给变量。(
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;
说:
- 取
c
的地址 .,其值是位模式1110 1101
。 - 将其视为类型
uint8_t*
的指针。 - 取消引用指针并返回生成的
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以"作者让编译器反转位"结束的原因。
- 比较并显示使用最小值(a,b)和最大值(a、b)升序排列的4个数字
- 为什么随机数生成器不在void函数中随机化数字,而在main函数中随机化
- 检查输入是否不是整数或数字
- 如何(从固定列表中)选择一个数字序列,该序列将与目标数字相加
- 如何用数字处理log(0)
- 最高有效数字侧的第N位
- 如何获取一个数字的前3位
- 查找最接近的大于当前数字的数字的索引
- 找到两对数字,使它们的乘积的绝对差最小化
- 我想做一个彼此不同但重复出现的数字
- 将数字转换为字母(例如:123 转换为一二三)
- C++如何计算用户输入的数字中的偶数位数
- 如何在C++中确定文本文件中的元素是字符还是数字
- 打印数字图案
- C++问题:用户认为数字1-100,程序提出问题不超过6次即可得到答案。无法正确
- 如何检查一个c++字符串中有多少相同的字符/数字
- 求出有多少个数字是完美平方,而sqrt()是L,R范围内的素数
- 将数字打印成文字
- 当使用比格式支持的精度更高的精度来显示数字时,会写出什么数据
- 在将数字随机生成为数组期间从内存输出随机数的数组