[深入浅出C语言]算术操作符和位操作符

353 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第25天,点击查看活动详情

前言

        本文就来分享一波作者对操作符的学习心得与见解,主要介绍算术操作符和位操作符。

        笔者水平有限,难免存在纰漏,欢迎指正交流。

关于操作符(运算符)

操作符分类

算术操作符

移位操作符

位操作符

赋值操作符

单目操作符

关系操作符

逻辑操作符

条件操作符

逗号表达式

下标引用、函数调用和结构成员

image.png

        操作符(运算符)对象是操作数(运算对象),不只是单单的一个数,实际上对象也可以是表达式的值。

算术操作符

加减乘除取模

  1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。

  2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。浮点数除法的结果是浮点数,而整数除法的结果是整数。

  3. 在C语言中,整数除法结果的小数部分被直接舍弃而非四舍五入,这一过程被称为截断。

  4. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。

image.png

        如果想了解关于取整、取余的更多内容可以看看我写的这篇文章:[深入浅出C语言]理解取整、取余和取模 - 掘金 (juejin.cn)

指数运算

        C没有指数运算符,不过标准库里提供了pow()函数用于指数运算:

函数原型

double pow( double x, double y );

所属头文件:

<math.h>

功能: 求取x的y次方的值,注意参数和返回值都是双精度浮点型

        比如pow(2.0, 3.0)得到的值是8.0。

要注意的取值:

image.png

移位操作符

        移位操作符的操作数只能是整数,并且移位针对补码操作。

左移操作符<<

移位规则:

        左边抛弃、右边补0,每移动一位相当于*2。

image.png

右移操作符>>

移位规则

        右移运算分两种:

1. 逻辑移位

        左边用0填充,右边丢弃

image.png

2. 算术移位

       左边用原该值的符号位填充,右边丢弃

image.png

       到底使用哪一种取决于编译器。

       VS使用算术右移。

警告⚠ : 对于移位运算符,不要移动负数位(如a << -1),这个是标准未定义的。

示例:

       是算术右移,还是逻辑右移?最高位补0,为何?

unsigned int d = -1;
printf("%d\n", d >> 1);
printf("%d\n", d >> 2);
printf("%d\n", d >> 3);

       答案是逻辑右移

image.png

位操作符

位操作符分类

&    //按位与,两个操作数二进制位都为1才为1,否则为0

 |   //按位或,两个操作数二进制位有1就为1,否则为0

 ^   //按位异或 ,两个操作数二进制位相同为0,相异为1

注:他们的操作数必须是整数,而且操作对象是补码!

示例:

#include <stdio.h>
int main()
{
 int num1 = 1;
 int num2 = 2;
 num1 & num2;
 num1 | num2;
 num1 ^ num2;
 return 0;
}

分析:

image.png

实例+分析+总结

不能创建临时变量(第三个变量),实现两个数的交换

方法一:

int a = 3;
int b = 5;
a = a + b;
b = a - b;//得到原来的a
a = a - b;//得到原来的b

方法二:

关于异或的小总结:3^3 = 0, 3^0 = 3

image.png

也就是说一个数异或它本身结果为0,一个数异或0结果为它本身。

       3^5^3 = 5,说明异或运算具有交换律,即3^5^3 = 3^3^5

所以可以这样:

int a = 4;
int b = 7;
a = a ^ b;
b = a ^ b;//也就是a^b^b=b^b^a=0^a=a得到原来的a
a = a ^ b;//也就是a^b^a=a^a^b=0^b=b得到原来的b

求一个整数存储在内存中的二进制中1的个数

方法一:

        循环进行以下操作,直到n被缩减为0。

        用该数据模2,检测其是否能够被2整除,若可以则该数据对应二进制比特位的最低位一定是0,否则是1。如果是1给计数加1,如果n不等于0,继续模2除2。(类比一下十进制)

        十进制数除10去掉一个十进制位,二进制数除2去掉一个二进制位。

image.png

int count_one_bit(int n)
{
    int count = 0;
    while(n)
    {
        if(n%2==1)
        count++;
        n = n/2;
    }
    return count;

}

        这样测不了负数,需要小小改动一下,把函数形参改为无符号整型unsigned int,当传入负数时,会自动转换类型,看成一个很大的正数。

缺陷:进行了大量的取模以及除法运算,取模和除法运算的效率本来就比较低。

方法二:

        一个数&1,若该数二进制第0位为1则结果为1,为0则结果为0

        那我们可以这样来设计,循环地把目标整数的二进制位右移,让每一个位上的数都&1,以此来判断每一位是不是1,结果为1则计数+1。正数负数都可以。

        实际上二进制位移位&1相当于取出每一位来判断。

int count = 0;
int a = 65;
for(int n = 0; n < sizeof(int); n++)
{
    if((a >> n)&1)
    count++;
}

方法三:

        或者这样(更巧妙高效):

int tmp = 0;
scanf("%d", &tmp);
int count = 0;
while(tmp)
{
    tmp = tmp&(tmp-1);
    count++;
}

        这样做每次与运算都会让目标数的二进制位减少最低位的一个1。

        为什么呢?

        每次-1,即n-1,最靠后的1变为0,它后面的0全变为1,这时候从这一位开始往后的所有位都与n上对应位相反,比如

image.png

         这时候n&(n-1)的话,dist前面的不变,后面的全变为0,得到的结果相较于n,最靠后的1就被消掉了。

image.png

         此种方式,数据的二进制比特位中有几个1,循环就循环几次,而且中间采用了位运算,处理起来比较高效。

拓展:判断一个数是不是2的n次方

2 ^ 0 = 1 -> 1

2 ^ 1 = 2 -> 10

2 ^ 2 = 4  -> 100

……

        我们发现2的n次方的二进制位上只有一个1,那么只要一个数的二进制位上去掉一个1后为0就说明它的二进制位上只有一个1,也就是2的n次方。

        if(n & (n - 1) == 0)条件成立说明是2的n次方。

两个int(32位)整数m和n的二进制表达中,有多少个位(bit)不同

思路一:

        把两个数各个二进制位都比较一下,用到了前面讲过的移位&1法

int count_diff_bit(int m, int n)
{
	int count = 0;
	int i = 0;
	for (i = 0; i < 32; i++)
	{
		if (((m >> i) & 1) != ((n >> i) & 1))
		{
			count++;
		}
	}
	return count;
}

思路二:

        直接用异或比较每一个二进制位,相异为1,再统计结果的二进制位有几个1,用n&(n-1)法

int count_diff_bit(int m, int n)
{
	int count = 0;
	//^ 异或操作符
	//相同为0,相异为1
	int ret = m ^ n;
	//统计一下ret中二进制位有几个1
	while (ret)
	{
		ret = ret & (ret - 1);
		count++;
	}
	return count;
}

获取一个整数二进制序列中所有的偶数位和奇数位

        其实是取出每一位的变式题,取出每一位就一位一位地右移,那取出奇数位或偶数位就两位两位地移,注意一下起点终点即可。这里从高位开始取出,如图

image.png

int main()
{
	int i = 0;
	int num = 0;
	scanf("%d", &num);
	//获取奇数位的数字
	for (i = 30; i >= 0; i -= 2)
	{
		printf("%d ", (num >> i) & 1);
	}
	printf("\n");
	//获取偶数位的数字
	for (i = 31; i >= 1; i -= 2)
	{
		printf("%d ", (num >> i) & 1);
	}
	return 0;
}

以上就是本文全部内容,感谢观看,你的支持就是对我最大的鼓励~

src=http___c-ssl.duitang.com_uploads_item_201708_07_20170807082850_kGsQF.thumb.400_0.gif&refer=http___c-ssl.duitang.gif