操作符部分相关知识介绍及使用



1 移位操作符

移位操作符分为:
<< 左移操作符和 >> 右移操作符
:移位操作符的操作数只能是整数,并且移动使不能移动负数位

左移操作符移位规则:
左边抛弃,右边补0
例如代码:

#include <stdio.h>
int main()
{
	int num = 10;
	int n = num<<1;//num虽然左移了一位,但是它的值不会改变,只会把移位后的值赋给n
	printf("n= %d\n", n);
	printf("num= %d\n", num);
	return 0;
}

在这里插入图片描述
右移操作符移位规则:
1.逻辑右移–右边抛弃,左边补0
2.算数右移–右边抛弃,左边补该值的符号位(0/1)

假如就把上面左移的代码左移操作符换成右移操作符,左边补的数就发生了变化。
分析如图:
在这里插入图片描述
在这里插入图片描述

2 位操作符

位操作符有:
& —按位与(两个二进制数都为1才出1,其余都出0),
| —按位或(两个二进制数有1出1,两个都是0才出0)
^ —按位异或(两个二进制数相异出1,相同出0)
~ —取反(二进制数如果是1,取反得到0,如果是0,取反得到1)
:位操作符的操作数也只能是整数

练习1:(^的使用)
不能创建临时变量(第三个变量),实现两个数的交换。
需要了解的:
1.^ 也满足交换律
2.两个相同的数异或得到的结果是0
3.0与任何数异或得到的是那个数本身
4.异或其实就相当于找不同,最终二进制结果的1的个数表示两个数有多少位不同。

代码如下:

#include <stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	printf("交换前:a = %d b = %d\n", a, b);
	a = a ^ b;
	b = a ^ b;			//b=(a^b)^b = a					
	a = a ^ b;			//a=(a^b)^a = b
	printf("交换后:a = %d b = %d\n", a, b);
	return 0;
}

代码的局限性:
1.只能作用于整数交换
2.代码的可读性差,阅读的人可能看不懂
3.代码执行的效率比创建第三个变量的方法低

练习2:编写代码实现:求⼀个整数存储在内存中的二进制中1的个数
方法1:
(该数取模2得到最低位,再除以2去掉当前的最低位。重复之前的操作直至该数变成了0)
代码如下:

#include <stdio.h>
int main()
{
	int n;
	int count = 0;
	scanf("%d", &n);
	while (n)
	{
		if (n % 2 == 1)
			count++;
		n /= 2;
	}
	printf("%d", count);
	return 0;
}

缺陷:不能计算负数中1的个数,因为负数取模2的值不可能等于1
但是负数在内存中存储时为二进制,符号位为负,至少有一个1.
例如-1,在内存中补码形式32个1

所以我们可以换种思路,尝试使用之前学习的移位操作符。

方法2:(移位操作符的使用)
(通过移位得到二进制数的每一位,然后要得到最低位可以&1)
因为1在内存中存储的二进制是整形32位,最低位为1,前面31位为0,任何数在内存中存储的二进制数&1,前面31位二进制数不管是什么与完1后前面的31位的0都会变成0(&只有两个数都为1才出1),最低位是什么&1之后就是它本身。

代码如下:

#include <stdio.h>

int main()
{
	int n;
	scanf("%d",&n);
	int i;
	int count = 0;
	for (i = 0; i < 32; i++)
	{
		if (((n >> i) & 1) == 1)	
			count++;
	}
	printf("count = %d", count);
	return 0;
}

这个代码还有优化的空间,因为无论输入的整数在内存中存储的二进制数有多少个1,循环都要执行32次,所以我们可以思考一下,如何才能够优化代码,使循环的次数和1的个数相同。

方法3:
思路:
可以使用n = n & (n - 1);
每次与完之后n在内存中存储的二进制数的最右边的1会被变成0。可以多举几个例子试一下。

代码如下:

#include <stdio.h>
int main()
{
	int n;
	scanf("%d", &n);
	int count = 0;
	while (n)
	{
		n = n & (n - 1);
		count++;
	}
	printf("count = %d", count);
	return 0;
}

学会了方法3的思路后,练习3的题目就能够轻松化解。

练习3:写一个代码,判断一个数是否是2^n
思路:
我们知道,一个二进制位上的位权就是2^n,所以如果一个数是2 ^n,那么它在内存中存储的二进制数肯定是由1个1和多个0组成的。所以只需要用练习2的方法三改变2 ^n二进制的1为0,再判断所以二进制位上的数是否都为0就能解决问题。

例如:10进制转2进制
2–2^1–10
4–2^2–100
8–2^3–1000
16–2^4–10000

代码如下:

#include <stdio.h>
int main()
{
	int n;
	scanf("%d", &n);
	if (n & (n - 1) == 0)
		printf("YES");
	else
		printf("NO");
	return 0;
}

练习4:(~的使用)
将一个数指定的二进制位置0或者置1
例如将13的二进制序列的第5位修改为1,然后再改为0(改完1再修改会0的时候需要用到取反

我们先思考二进制序列是怎么变化的。(每8位分开方便观察)
(1)13的二进制序列:
00000000 00000000 00000000 00001101
00000000 00000000 00000000 00010000
(推导从(1)–>(2),将1<<4与(1)按位或即可得到)
(2)将第5位置为1后:
00000000 00000000 00000000 00011101
00000000 00000000 00000000 00010000
(我们想让第五位的1变成0,但是其他1不变,显然再用1<<4做不到)
这时后就要用到取反了,用~1(11111111 11111111 11111111 11101111)按位与来实现
(3)将第5位改回0后:00000000 00000000 00000000 00001101

代码如下:

#include <stdio.h>
int main()
{
	int a = 13;
	a = a | (1 << 4);
	printf("%d\n", a);
	a = a & ~(1 << 4);
	printf("%d\n", a);
	return 0;
}

结果如图:
在这里插入图片描述

3 逗号表达式

逗号表达式,就是用多个逗号隔开的表达式。
规则:从左往右依次执行,整个表达式执行的结果是最后一个表达式的值
例如:

int a = 10;
int b = 20;
int c=(a=a+b,b=a-b,a+b);

结果如图:
在这里插入图片描述
不要想当然的认为逗号表达式最后一个表达式前面的表达式没有用,如果只用最后一个表达式,那么c的值应该为a+b=30。实际上这题的过程应该是:
(1)a=a+b=20+10=30,(2)b=a-b=30-20=10,(3)c=a+b=30+10=40

4 表达式求值

4.1 整形提升

在整形运算时,表达式的字符和短整型数在使用前会被转换成整形(方便CPU执行运算),转换的过程就称为整形提升。
规则:
1.有符号整数提升按照变量的数据类型的符号位来提升的,符号位如果为1的话,高位就补1。
2.无符号整数提升,高位补0

例如用char类型计算两个数的相加:

#include <stdio.h>
int main()
{
	char a = 5;
	//5是int类型,4字节
	//00000000 00000000 00000000 00000101
	//因为char是1个字节,所以00000101截断后(就是取最后8个二进制数)存储给a
	char b = 127;
	//00000000000000000000000001111111
	//01111111截断后存储给b
	char c = a + b;
	//表达式中的字符和短整型操作数在使用之前被转换为普通整型,a,b是有符号正数,所以前面补符号位0
	//00000000 00000000 00000000 00000101---a
	//00000000 00000000 00000000 01111111---b
	//00000000 00000000 00000000 10000100---a+b
	//10000100---c
	printf("%d", c);		
	//c要整形提升,高位补符号位1
	//11111111 11111111 11111111 10000100---补码形式,要转换成原码再转换成十进制输出(除符号位外剩余的二进制数取反加1即可)
	//10000000 00000000 00000000 01111011(取反)
	//10000000 00000000 00000000 01111100(加1)--->结果为-124
	return 0;
}

4.2算数提升

简单来说就是排名靠后的操作数类型要转换成排名靠前的操作数的类型再执行运算。
寻常的算数转换如图:
在这里插入图片描述
一般都是下面的类型往上面的类型转换。

5 一些问题表达式

例如代码1:

c+ --c;

我们知道- -的优先级是是高于+的,但是我们无法确定运算时左边的c的值到底是什么。假如c=3,- -c就是2,那么这个表达式到底是3+2呢还是2+2呢,无法确定左边的c的值,(在不同编译器运行的结果可能不同)所以我们在写代码的时候,还是最好一步步来,避免写这种模棱两可的代码。

代码2:

#include <stdio.h>
int main()
{
 int i = 1;
 int ret = (++i) + (++i) + (++i);
 printf("%d\n", ret);
 printf("%d\n", i);
 return 0;
}

这个代码ret右边i的值无法确定。

在VS上运行是首先计算3个++i,i的值得到4之后,再ret=4+4+4=12.
在这里插入图片描述
但是如果是在其他编译器上,例如gcc编译器:
在这里插入图片描述
gcc编译器的计算过程是第一个++i,得到i=2,第二个++i,得到i=3,然后
ret = 3+3+(++i)=6+(++i),再计算第三个++i,得到i=4,最终ret=6+4=10。

在两种编译器底下得出了两种不同的结果,各有各的计算思路,但是结果不一致,出现问题。

总之,我们在写代码的时候,要多注意代码的规范性和可读性,最终再去追求代码的高效性。要养成良好的写代码的习惯,这会对我们今后学习代码很有帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值