我之前的文章《C语言——操作符》中有详细讲解位操作符。
例如5的二进制补码是00000000 00000000 00000000 00000101,这之中的1的个数为2
#include
int Counter(int x)
{
int i = 0,k = 1,counter = 0;
for (i = 0; i < 32; i++)
{
if ((x & k) != 0)
{
counter++;
}
k <<= 1;
}
return counter;
}
int main()
{
int n = 0;
scanf("%d", &n);
printf("%d\n",Counter(n));
return 0;
}
因为位操作符&的规则是两个二进制位都是1结果才能是1,所以我们,可以对参数的二进制每一位进行按位与操作,只要按位与后的结果不是0,则这一位是1,计数器就加一,这样就可以计算1的个数,可以将1不断向左移位,实现每一位都被用1按位与操作。
0101 & 0001结果不是0,则第一位是1,0101 & 0010结果是0,则第二位是0。
从0001到0010就需要用到左移操作符。
运行结果:
#include
int Counter(int x)
{
int i = 0,counter = 0;
for (i = 0; i < 32; i++)
{
if (((x >> i) & 1) == 1)
{
counter++;
}
}
return counter;
}
int main()
{
int n = 0;
scanf("%d", &n);
printf("%d\n",Counter(n));
return 0;
}
对于这个方法是直接将参数右移然后按位与1,参数右移32位后会将参数的每一位都按位与1,只要按位与1后等于一,则这一位是1,等于零的话,则这一位是0,这样就可以计算1的个数。
0101 & 0001结果为1,则第一位是1,然后将参数右移一位得到0010,0010 & 0001的结果为0,则第二位为0。
右移32位后就能将参数的每一位都按位与1,则可以计算1的个数。
运行结果:
对于这个题目,不用位操作符一样能实现,类比十进制中将一个数的每一位相加,这里也能用取余和除实现。
#include
int Counter(unsigned int x)//这里用无符整型是为了适用于负数
{
int counter = 0;
while (x)//只要x不为0,则循环进行
{
if (x % 2 == 1)
{
counter++;
}
x /= 2;
}
return counter;
}
int main()
{
int n = 0;
scanf("%d", &n);
printf("%d\n",Counter(n));
return 0;
}
对于十进制数字5,它的二进制补码为0101,两个1。
5 % 2 = 1,counter++
5 / 2 = 2 ... 1
2 % 2 = 0
2 / 2 = 1
1 % 2 = 1,counter++
1 / 2 = 0 ... 1
得到结果为2,所以有两个1。
对于无符整型为了适用于负数,如果输入-1,-1的补码为11111111 11111111 11111111 11111111,由于函数参数用的是无符整型,则这个补码会被识别为一个很大的整数,即4294967295,最终会得到结果为32个1,这是正确结果,-1的补码就是32个1。
这里如果不使用无符整型而是用int类型,则-1的补码不会被识别成一个很大的正数,而是被识别成-1,而-1的流程为:
-1 % 2 = -1
-1 / 2 = 0 ... -1
结果为0,这个结果是错误的,因为-1的补码有32个1。
一个很神奇的方法。
#include
int Counter(int x)
{
int counter = 0;
while (x)
{
x = x & (x - 1);
counter++;
}
return counter;
}
int main()
{
int n = 0;
scanf("%d", &n);
printf("%d\n",Counter(n));
return 0;
}
一个十进制数字7,他的二进制补码为0111。
0111 & 0110 = 0110
0110 & 0101 = 0100
0100 & 0011 = 0000
每次进行x = x & (x - 1)操作后,x最右侧的1就会变成0,这样只要每一轮加一,直到x变成0再停止,这样就可以计算1的个数。
运行结果:
例如0100和0101有一位不同。
直接比较两个数的每一位是否相同。
#include
int Func(int x,int y)
{
int i = 0,counter = 0;
for (i = 0; i < 32; i++)
{
if (((x >> i) & 1) != ((y >> i) & 1))
{
counter++;
}
}
return counter;
}
int main()
{
int m = 0,n = 0;
scanf("%d %d", &m, &n);
printf("%d\n",Func(m,n));
return 0;
}
用右移操作符将两个数的每一位都按位与1,如果按位与1的结果相同,则两个数的这一位是相同的,如果按位与1的结果不相同,则两个数的这一位是不相同的,不相同则加一,这样可以计算不同的位有多少个。
运行结果:
#include
int Counter(int x)
{
int counter = 0;
while (x)
{
x = x & (x - 1);
counter++;
}
return counter;
}
int Func(int m,int n)
{
return Counter(m ^ n);
}
int main()
{
int m = 0,n = 0;
scanf("%d %d", &m, &n);
printf("%d\n",Func(m,n));
return 0;
}
按位异或的规则是相同为0,不同为1,只用按位异或两个数然后统计1的个数,就可以实现要求,这又与上面的计算1的个数联系起来了。
运行结果:
例如0101,奇数位为第一和第三位,打印出即11,偶数位为第二位和第四位,打印出来即00。
将每位都按位与1,按位与1会得到这一位上的数字,再通过右移操作符即可实现打印每一位的数字,再进行一些调节则可实现打印奇数和偶数位上的数字。
#include
int main()
{
int num = 0;
int i = 0;
scanf("%d", &num); //右移30位是直接将31位放到第一位,i -= 2实现后面的奇数位移到第一位,i >= 0则是最小只移动0位,则是将第一位移到第一位,就是没移动
for (i = 30; i >= 0; i -= 2)//这里反向生成是为了从高位向低位打印,方便阅读
{
printf("%d", (num >> i) & 1);
}
printf("\n"); //右移31位是直接将32位放到第一位,i -= 2实现后面的偶数位移到第一位,i >= 1则是最小只移动1位,则是将第二位移到第一位
for (i = 31; i >= 1; i -= 2)//这里反向生成是为了从高位向低位打印,方便阅读
{
printf("%d", (num >> i) & 1);
}
printf("\n");
return 0;
}
十进制数字5的二进制补码是00000000 00000000 00000000 00000101
运行结果:
全局变量和静态变量都在静态区,如果全局变量或静态变量不初始化的话,则被默认初始化为0。
局部变量在栈区,而局部变量不初始化则里面放的是随机值。
#include
int i;
int main()
{
i--;
if (i > sizeof(i))
{
printf("大于!\n");
}
else
{
printf("不大于!\n");
}
return 0;
}
这里的运行结果为:
因为全局变量不初始化,则被默认初始化为0,在通过i--时,i变成了-1,那sizeof(i)应该为4啊!为什么会输出大于呢?这是因为sizeof输出的结果为size_t类型,即无符整型,而且当有符号和无符号整型混合运算时,如果无符号类型不小于有符号类型,则有符号类型会转换为无符号类型。这个在我之前的文章《C语言——表达式的求值》中提到过。
-1的二进制补码为11111111 11111111 11111111 11111111,这里i被转换为无符号整型,则-1的二进制补码被识别成了很大的正数,即4294967295,4294967295 > 4所以会输出大于的信息。