C语言——小细节和小知识8

发布时间:2024年01月07日

一、位操作符的运用

我之前的文章《C语言——操作符》中有详细讲解位操作符。

例1:写一个函数计算参数二进制中的1的个数

方法一:

例如5的二进制补码是00000000 00000000 00000000 00000101,这之中的1的个数为2

#include <stdio.h>

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 <stdio.h>

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 <stdio.h>

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 <stdio.h>

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的个数。

运行结果:

例2:计算两个数的二进制补码有多少位是不同的

例如0100和0101有一位不同。

方法一:

直接比较两个数的每一位是否相同。

#include <stdio.h>

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 <stdio.h>

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的个数联系起来了。

运行结果:

例3、获取一个二进制序列的奇数位和偶数位,并打印,假设最低位为第一位

例如0101,奇数位为第一和第三位,打印出即11,偶数位为第二位和第四位,打印出来即00。

将每位都按位与1,按位与1会得到这一位上的数字,再通过右移操作符即可实现打印每一位的数字,再进行一些调节则可实现打印奇数和偶数位上的数字。

#include <stdio.h>

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 <stdio.h>

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所以会输出大于的信息。

文章来源:https://blog.csdn.net/stewie6/article/details/135423737
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。