目录
今天阅读《程序员面试宝典》时,发现x & (x-1)是一个十分重要且有意思的知识点,在此与各位分享一下
主要内容包含两个知识要点:
(1)局部变量覆盖全局变量时,要使用全局变量需加【::】修饰符
(2)x & (x-1)隐藏的秘密
直接上代码:
#include <iostream>
using namespace std;
int i = 0;
int func(int x)
{
while(x)
{
x = x & (x-1);
i++;
}
return i;
}
int main()
{
int i = ::i;
cout << func(9999) << endl;
cout << i << endl;
return 0;
}
1 局部变量对全局变量的隐藏问题
在main函数代码中,我们使用全局变量 i 为同名的局部变量 i 赋值,使用到了【::】运算符;如果没有改运算符,会报以下错误:
这也是一个比较经典的面试题,值得关注一下。
2 x & (x-1)的使用分析
func函数的作用实际上是获得定参数x转为二进制时包含的1的个数,代码执行过程如下:
3 拓展分析
3.1 利用按位与运算判断一个数是否为2的幂次
由此可以引伸出,若要判断某个数字是否为2的幂,可采用以下函数实现:
bool isTwoPower(int x)
{
if((x & (x - 1)) == 0)
{
return true;
}
else
{
return false;
}
}
3.2 利用按位或判断二进制数中0的个数
int getZeroCnt(int x)
{
int cnt = 0;
while(x + 1)
{
x = x | (x + 1);
cnt++;
}
return cnt;
}
3.3 简单循环方法计算0和1的个数
//得到一个数转化为二进制序列后包含的0的个数
int getZeroCnt1(int num)
{
int count = 0;
while(num)
{
if(num % 2 == 0)
{
count++;
}
num /= 2;
}
return count;
}
//得到一个数转化为二进制序列后包含的1的个数
int getOneCnt1(int num)
{
int count = 0;
while(num)
{
if(num % 2 == 1)
{
count++;
}
num /= 2;
}
return count;
}
//得到一个二进制数字包含的二进制位数
int getBits(int num)
{
int count = 0;
while(num)
{
count++;
num /= 2;
}
return count;
}
3.4 与1进行按位与的方法
将这个数与1进行按位与,如果结果是1,那么这个数的最末位就是1,判断完最末位,再将这个数进行右移运算,判断倒数第二位,如此循环32次,就判断出了这个数二进制序列中有多少个数字1。这个方法弥补了模2除2法只能判断正数的缺陷。
int getOneCnt2(int n)
{
int count = 0;
int i = 0;
for (i = 0; i < 32; i++)
{
if (((n >> i) & 1) == 1)//一个数与1按位与结果如果是1则这个数的最末位就是1
{
count++;
}
}
return count;
}
int getZeroCnt2(int n)
{
int count = 0;
int i = 0;
for (i = 0; i < 32; i++)
{
if (((n >> i) & 1) == 0)
{
count++;
}
}
return count;
}