初始C语言位操作符以及应用分析

作为一位刚刚入门的小菜狗,我经常好奇位操作符有些什么用呢??

通过一段时间的学习,我发现在一些问题上如果采用位操作符,处理问题的能力可以得到大幅提升。下面是我小小总结的,希望可以帮助大家更好的理解这些操作符,如果大家对于文章有更好的建议的话,也欢迎提出。

这是位操作运算符的基本定义,那我接下来给大家带来我的理解。希望对大家的理解有所帮助。(需要注意的是,这些操作符都是在二进制的形式下进行的)

操作符 &

它的功能是 1 &1 =1,1& 0 = 0,0 & 1 = 0,0 & 0 = 0.可以这样理解,有0 则0 ,无0 则1 .或者把它与操作符&&联系起来进行记忆。1代表真,0表示假,全真则真,有假则假。

 

操作符 |

它的功能是 1| 0 =1,1|1=1,0|0=0,0 | 1 = 1;可以这样理解,有一则1 ,无一则0 ,或者同样的办法把它与操作符|| 联系起来进行记忆,1代表真,0表示假,有真则真,全假则假

 

 

操作符^

它的功能是1 ^1 = 0,0^ 0=0,1^ 0 =1.相同为0 ,相异为一。

这个特性决定了,如果两个相同的数字进行异或,由于每个二进制位上的数字都相同,所以两者异或之后所有的二进制位的数全变为了0,故a^a = 0;如果a^0= 0,因为a对应位置上的1^0 = 1,0^0=0;我们发现在这个过程中,a的二进制位上的数字都没有发生改变。

同时它拥有交换律

结合律

(a^b)^c =a^(b^c);

a^a=0。

 

 

操作符~

就是0变为1 ,1变为0.

 

左移操作符<<

可能会有同学疑惑了,如果一个数字左移之后,这个数字本身是否会发生改变,答案是否定的,可以举个通俗的例子,如果  int i = 1; i+1的情况下只要没有赋值给它本身,则这个值就不会发生改变。

 

 右移操作符>>

 

 逻辑移位

 

算数移位

 

位操作只能用于整形数据,对float和double类型进行位操作会被编译器报错。

学完之后,想了半天,不知道它们有什么实际意义。

接下来是我通过一段时间的学习,发现的代码学习过程中一些妙用。这些东西是我在学习过程中,自己进行经验总结的,可能存在不足,望大家多多指正。

接下来的前面一部分是单个运用实例,后面是组合运用实例。

先来看看操作符 & 。

  1. 由于它的特点,我们可以利用这个特性,可以实现屏蔽某些特定位置的二进制位。(保留某些二进制位)

  2. 判断一个数据的某一位是否为1或者0,我们可以利用这个特点实现对一个数据是奇偶性,

  3. 任何一个正数在二进制的情况下减一,我们会发现这个数(二进制的情况)最右边的1向右移动了一位,如若我们这时进行操作 n= n&(n-1),我们发现除了只有两个相对应的位置数据不同,其余位置的数据还保持一样,我们通过画图分析,发现这个这个语句的功能去除一个数字(二进制)的最右边的一个一。所以这个语句有什么作用,我们可以利用这个语句来计算一个数字的二进制中的一。

    如一些题 ,给出一个正数字,统计其二进制中一的个数。

  • 先来观察第一个

 

  • 然后第二个
int main()
{
	int a = 0;
	scanf("%d", &a);
	int i = a & 1;
	if (i == 1)
	{
		printf("奇数\n");
	}
	else
	{
		printf("偶数\n");

	}

	return 0;
}

  •  第三个

具体的分析就放在了代码注释中,下同。

int main()
{
	int a = 1235;
	int i = 0;
	int count = 0;
	for (i = 0; i < 32; i++)
	{
		if (a % 2 == 1)
		{
			count++;
		}
		a = a / 2;
		if (a == 0)
		{
			break;
		}
	}
	printf("%d\n", count);
	return 0;
}
//常规方法

// 新方法
int main()
{
	int a = 1235;
	int i = 0;
	while (a != 0)
	{
		a = a & (a - 1);
		i++;
	}
	printf("%d\n", i);
	return 0;
}

再来看看操作符 | 。

  • 我们可以将某个数据的特定位置的数字置为1.

 接下来是操作符~

我们在一些题目中,经常要实现多组输入,那我们通常可是通过这种方法进行实现。

 

我们先来了解一下这scanf函数,大家可能表示这个不就是输入函数,那我们看一下scanf的文档,发现它是有返回类型的,那我们在这里稍微跑一下题,带大家了解一下这个函数,

 

 发现如果返回值为 EOF(end-of-file)时,这个函数就结束了,如果读取成功,则返回读取成功元素的个数。

 

 

 回到正题,EOF的值其实为  -1   ,

 故我们可以这样实现多组输入

int main()
{
	int  i = 0;
	while(~scanf("%d", &i))//while (scanf("%d", &i) != EOF)
	{
		printf("%d\n", i);
	}
	return 0;
}

接下来是操作符^

  1. 还有啥作用呢??如果一个题要求我们不准创建临时变量,来交换两个数的值,你会有多少种方法。

    目前的我有两种方法,后续有新的方法,我会更新给大家的。

    第一种方法,

    a= a+b;b = a - b;a= a-b;

    第二种

    利用操作符异或,我们发现两个可以利用异或进行操作。

  2. 我们也可以寻找一个数组中的数字,这个数组有什么特点呢?这个数组里面存储的数,只有一个数在数组中的个数为奇数个。其他的数在数组中的个数为偶数个。例如[1,1,2,2,,3]

    或者[1,1,1,1,2,2,3],我们的目标就是找出那个为奇数个数的数字(这个可以理解位寻找单个数)。就可以利用这个操作符。

  • 第一个
int main()
{
	int a = 10;
	int b = 20;
	//交换数值第一种方式
	//1.最常用的方法
	/*int tmp = a;
	a = b;
	b = tmp;*/

	//不创建临时变量 
	//1.第一种方法
	/*a = a + b;
	b = a - b;
	a = a - b;*/
	//2.第二种方法
	a = a ^ b;//1.
	b = a ^ b;//2.
	a = a ^ b;//3.
  //我在这里给大家分析一下这个方法   
  //进行1.之后 a = a ^ b ,进行第二步(2.)操作(进行替换),b = a ^ b = (a ^ b) ^b = a ^ b ^ b = a ^ 0 = a;   
  //第三步(3.)也是同理,a = a ^ b = (a ^ b) ^ a = a ^ a ^ b = 0 ^ b = b
  //这里的思想主要是替换,大家要明白a  b 在什么时候代表的是什么?  
  //利用异或方法,大家可以利用一些简单的数字进行计算 ,印象更深刻
	return 0;
}
  • 第二个

int main()
{
	int a[5] = { 1,1,2,2,3 };
	int sum = 0;
	for (int i = 0; i < 5; i++)
	{
		sum = sum ^ a[i];
	}
	printf("%d\n", sum);
}

 接下来是操作符左移和右移。

我们发现左移是*2的效果,右移的话分为两种情况。就具体而看。

接下来进行复合运算。

我们需要将一个int类型的数据的奇数位和偶数位进行一个交换。大家可以自行敲一下,然后在看一下我的代码。常规方法我就不写了哈。(思路在代码中)

int main()
{
	int i = 0;
	scanf("%d", &i);
	int sum = ((i >> 1)&0x 00 00 55 55) + ((i << 1)&0x aa aa aa aa);
	//假设 i =  00000000 00000000 11001011 01101010(二进制表示) 
	//   i>>1   针对的是 i 的偶数位   ,右移后需要利用&操作符将原来的偶数位进行置零,
	//      00000000 00000000 01100101 10110101   右移之后  
	//      01010101 01010101 01010101 01010101   然后与此数进行&  此数为 0x 00 00 55 55 
	//      00000000 00000000 01000101 00010101   通过观察可以发现i的偶数位移动到了奇数位,并且使奇数位 置为0
	//      00000000 00000001 10010110 11010100   左移之后
	//      10101010 10101010 10101010 10101010   然后于此数    此数为  0x aa aa aa aa 
	//      00000000 00000000 10000010 10000000   通过观察可以发现i的奇数位移动到了奇数位,并且使偶数位 置为0
	//然后把奇数位与偶数位
	printf("%d\n", sum);
	return 0;
}

然后呢,我们在来解决寻找单个数的plus版,数组中有两个数在数组中的个数为奇数个。具体分析过程我就写入代码中了。(注释部分)

int main()
{
	int arr[10] = { 1,1,2,2,3,3,4,4,5,6 };
	//以这个数组为例,我们发现 5 6这两个数字是我们所需要的。
	//我们发现这次有两个单个的,那我们这个怎么进行往后进行呢??
	//我们可不可以将这个数组分为两组,将两个单个的数字分开在两个组里面,
	// 当然对这个数组还有啥要求呢??
	//成对的数字应该被分到同一组里面,不然无法进行消去。
	//就以这个数组为例,可以分为
	// {1,1,3,3,5} {2,2,4,4,6}或者
	// {1,1,5} {2,2,3,3,4,4,6}都可以 
	// 只要满足我们的两个条件 1. 两个单个的数字分到两组, 2.成对的数字就放在一组
	//那么我们应该怎样将这两个单个的数字分到两个数组呢
	//那我们就要想到我们的操作符,哪一个操作符可以将两个不同的数字分开
	//没错就是我们的异或 
	//先将这两个数字异或,找到二进制位上不同的数
	// 例如异或之后的结果为 1010 ,我们根据这个结果可以推断出 ,这两个数字(二进制表示下)第二位与第四位不同
	// 可以作为将两个数字分开的一个依据。
	// 但是成对的数字怎样分到两组呢???数字相同,二进制位也相同。
	// 我们需要注意的是,我们只是需要将成对的数字分在两个数组里,至于每个数字里面分多少,我们不太关注,因为最后通过运算消去了,
	// 只需要保证成对的数字分在一起。那怎么分呢???什么作为分组的依据呢???随缘分法吗???
	// 其实我们可以依据前面   单独的数字的分组依据,   将这个作为依据进行分组。然后这就是大概思路
	// 
	// 
	// 
	// 来来,我们整理一下思绪  ,然后上手,
	// 1.找到二进制位中的不同的位数
	// 2. 按照这个不同的位数,将整个数组分成两个数组。   
	//  需要注意的是,在2. 这个过程中,那两个  单的数字   已经分到两个边,顺带也将成对的数字分组,虽然不能保证两边数组元素
	//  个数相同,但是能保证成对的数字都放在了一边。
	


	//寻找不同位数
	int i = 0;
	int sum = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < sz; i++)
	{
		sum ^= arr[i];
	}
	//上面的话  已经将所有的数字已经异或。
	//接下来寻找sum中的  1 的位置  ,找到一个就可以。因为异或的话,1表示两个元素不一样  ,可以作为我们分组的依据。
	i = 0;
	while (((sum >> i) & 1) != 1)
	{
		i++;
	}
	//假如sum为下
	//    sum =       00000000 00000000 00000000 00101000
	//      1         00000000 00000000 00000000 00000001
	//                00000000 00000000 00000000 00000000
	//两者就进行&运算 ,当运算结果为0 时,证明此时两个数字这个二进制位相同,则将sum右移一位,
	// 依次类推     
	//当 ((sum >> i) & 1)  = 1 ,此时sum右移了i位,证明两个数字的第i位数据不同 ,可以将此位作为一个分组的依据。
	
	
	
	// 5  101
	// 6  110
	//    011
	int arr1[10] = { 0 }, a1 = 0;//由于我们不知道两个数组每个数组分配多少元素,所以呢,尽可能的创建大的数组
	int arr2[10] = { 0 }, a2 = 0;//
	int a = 0;
	for (a = 0; a < sz; a++)
	{
		if ((arr[a] >> i) & 1)
		{
			arr1[a1] = arr[a];
			a1++;
		}
		else
		{
			arr2[a2] = arr[a];
			a2++;
		}
	}
	//通过上面的分类,已将数字分到了两个数组,接下来分别进行异或
	i = 0;
	int x = 0;
	for (i = 0; i < a1; i++)
	{
		x = x ^ arr1[i];
	}
	printf("%d\n", x);
	i = 0;
	  x = 0;
	for (i = 0; i < a2; i++)
	{
		x = x ^ arr2[i];
	}
	printf("%d\n", x);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值