操作符
- 一,操作符的分类
- 1,算数操作符
- 2,赋值操作符
- 3,逻辑操作符
- 4,条件操作符
- 4,单目操作符
- 5,函数调用和下表访问操作符
- 二,原码反码补码
- 三,移位操作符
- 1,左移操作符
- 2,右移操作符
- 四,位操作符
- 1,&按位与的妙用
- 2,|按位或的妙用
一,操作符的分类
C语言中提供的操作符有一下这么几种:
• 算术操作符: + 、- 、* 、/ 、%
• 移位操作符: << >>
• 位操作符: & | ^
• 赋值操作符: = 、+= 、 -= 、 = 、 /= 、%= 、<<= 、>>= 、&= 、|= 、^=
• 单⽬操作符: !、++、–、&、、+、-、~ 、sizeof、(类型)
• 关系操作符: > 、>= 、< 、<= 、 == 、 !=
• 逻辑操作符: && 、||
• 条件操作符: ? :
• 逗号表达式: ,
• 下标引⽤: []
• 函数调⽤: ()
• 结构成员访问: . 、->
看到这些操作符相信有一些是你已经见过并熟练掌握的,但还有一些涉及到位运算的操作符你可能是初次见到。像移位操作符,逗号表达式,位操作符等,本篇文章重点来讲述涉及到位的操作符。
我们先来花小部分篇幅来讲算术操作符、赋值操作符、逻辑操作符、条件操作符和部分的单⽬操作符。
1,算数操作符
算术操作符有这几种分别为: + 、- 、* 、/ 、%唯一需要注意的就是取模运算,计算结果得到的是余数。
通过重复除10模10(/10%10)这两个算数运算符可以获得一个多位数的每一位!
2,赋值操作符
赋值操作符有以下几种分别为:= 、+= 、 -= 、 *= 、 /= 、%= 、<<= 、>>= 、&= 、|= 、^= 。这种写法其实是n=n+m简写成n+=m的写法。赋值运算符要和关系运算符分开,很多初学者搞不清一个等号和两个等号。
一个等号 = -> 赋值
两个等号 ==-> 判断相等
其中包含的<<,>>,&,^是稍后要介绍的设计位的操作符。
3,逻辑操作符
逻辑操作符有两种: && 、||。&&叫逻辑与 ||叫逻辑或
逻辑操作符主要关心的是布尔值,布尔值True表示真,False表示假,而计算机是二进制只有0和1来表示所以1表示真,0表示假。
所以逻辑操作符在运算的时候有几种情况: 0&&0为0 1&&0为0 1||0为1 0||0为0
在学逻辑操作符还要记住一句话,逻辑与有假为假,逻辑或有真为真,非零即真。
4,条件操作符
条件操作符主要为: ? : 。这个操作符具体形式为:表达式?结果一:结果二。
举个简单的例子:
int main()
{int a = 3;int b = 5;int c = 0;c = a > b ? a : b;printf("%d", c);printf("\n");return 0;
}
从运行结果来看得到的值为5,那为什么是5呢/我们来分析一下代码:
a>b?a:b 从左到右?意思是表达式左边的值为真吗?也就是a是大于b的吗?如果大于结果为a,再将a赋值给c,如果小于,结果为b,再将b赋值给a。
总结:
所以 表达式?结果一:结果二; 表达式为真,执行结果一;表达式为假,执行结果二。
4,单目操作符
单⽬操作符有以下几种: !、++、–、&、*、+、-、~ 、sizeof、(类型)。其中也有一些位操作符,我们稍后介绍。还有一些像自增,自减已经在之前就介绍过。其中比较重要的就是sizeof和~取反操作。
1. sizeof是用来计算类型大小的,比如sizeof(int/short/long/double)。sizeof也经常用来计算数组元素的个数。
**2. ~取反操作,就是将以二进制形式存储的数据进行取反操作。**这点等介绍完了原码反码补码之后你就会明白了。
3,还要注意的是如果sizeof里面有表达式是不参与计算的,因为sizeof在编译阶段就直接返回类型的大小了,比如int类型大小为4。而表达式计算是在运行时才计算的。
5,函数调用和下表访问操作符
函数调用和下标访问操作符在之前的文章有讲到过,可以参考之前的文章。
传送门函数:函数
传送门数组:数组
二,原码反码补码
在介绍设计位操作符之前我门先要了解数据在计算机的存储形式,上一篇文章我们详细介绍了进制转换,有了进制转换的知识就为我们下面的学习打下了基础。如果还不了解进制转换可以去看上一篇文章:传送门。
在上一篇文章中我们介绍了计算机的底层实际上就是电路,而电路只有高电平和低电平两种状态这就与二进制相似只有0和1这两种状态。这时可能有人问计算机是怎么存储数据的呢?比如在我们写代码的时候我们通常会定义一个变量然后给它赋值,那这个值是怎样存储的呢?
首先来介绍一下什么是原码,反码,补码:
1. 原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
2. 反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
3. 补码:反码+1就得到补码。
整数的2进制表示方法有三种,即原码、反码和补码 有符号整数的三种表示方法均有符号位和数值位两部分,2进制序列中,最高位的1位是被当做符号 位,剩余的都是数值位。 符号位都是⽤0表⽰“正”,⽤1表⽰“负”。一个整型数是占4个字节 一个字节占8比特位 所以是由32位二进制来构成 举个例子:
而整数又分为有符号整数和无符号整数这两种,而有符号数又分为正整数和负整数这两种。
- 正整数的原、反、补码都相同。
- 负整数的三种表方法各不相同。
- 负整数取反加一为补码 补码取反加一为原码。
我们来看一个简单的运算:
#include<stdio.h>
int main()
{int a = 10;int b = -5;//10的原码00000000000000000000000000001010//因为10为整数 原码=反码=补码//-5的原码10000000000000000000000000000101//-5的反码11111111111111111111111111111010//-5的补码11111111111111111111111111111011int c=a+b;//a+b 是补码运算//10的补码 00000000000000000000000000001010//-5的补码 11111111111111111111111111111011//相加之后的补码:100000000000000000000000000000101 最左边多出的一位将丢弃掉所以符号位为0是正整数 原反补都相同,直接读出101对应的值是5printf("%d",c);return 0;
}
由此我们可以总结:
但是数据存放内存中其实存放的是补码这是为什么呢?
在计算机系统中,数值⼀律⽤补码来表⽰和存储。原因在于,使⽤补码,可以将符号位和数值域统⼀ 处理;同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算 过程是相同的,不需要额外的硬件电路。
三,移位操作符
1,左移操作符
移位规则:左边抛弃、右边补0;来看一段代码:
#include<stdio.h>
int main()
{int num=10;int m = num<<1;printf("num = %d",num);printf("m = %d",m);return 0;
}
从结果上看不知各位读者看出来了吗就是左移是不会改变原来的值。我们给出具体分析
2,右移操作符
移位规则:⾸先右移运算分两种:
- 逻辑右移:左边⽤0填充,右边丢弃
- 算术右移:左边⽤原该值的符号位填充,右边丢弃
一般情况下都是算数右移动。下面来看代码;
#include <stdio.h> int main() { int num = 10; int n = num>>1; printf("n= %d\n", n); printf("num= %d\n", num); return 0; }
//逻辑右移1位演⽰ 算术右移1位演⽰
//警告:对于移位运算符,不要移动负数位,
//这个是标准未//定义的。 例如:1 int num = 10;
我们分别给出逻辑右移和算数右移:
总结:
移位操作符操作数只能是整数!不能是其它的数!
四,位操作符
位操作符有三种分别是: &(按位与) |(按位或) ^(按位异或)~(按位取反)。其中按位的位指的是二进制位。举个例子;
#include<stdio.h>
int main()
{int a = -3;int b = 6;int x = 0;int y = 0;int r = 0;int c = 0;x = a & b;y = a|b;r = a^b;c = ~a;printf("a&b = %d\n",x);printf("a|b = %d\n",y);printf("a^b = %d\n",r);printf("~a = %d\n",c);return 0;
}
紧接着我们来分析为什么是这个结果。大家请看图
通过图片已经能非常直观的能看清楚每一步的运算过程了。但有一点要注意就是按位操作符他们的操作数必须是整数。
学习完上面的知识后我们来看一道比较变态的面试题,题目是不创建第三变量怎样实现两个数的交换。这里我们不卖关子直接给出代码然后分析:
#include<stdio.h>
int main()
{int a = 3;int b = 6;printf("交换前a=%d b=%d\n",a,b);a = a ^ b;b = a ^ b;a = a ^ b;printf("交换后a=%d b=%d",a,b);return 0;
}
是不是很神奇,那为什么这样就能直接交换了呢?我们给出相应的图解;
紧接着我们扩展学习一些按位操作符的妙用。
1,&按位与的妙用
我们用一道编程题来举例,求⼀个整数存储在内存中的⼆进制中1的个数。(一下都以这道题为例)我们直接给出代码:
#include<stdio.h>
int main()
{int n = 0;scanf("%d",&n);int count = 0;while(n){if(n%2==1){count++;}n=n/2;}printf("count=%d",count);return 0;
}
15的二进制序列为00000000000000000000000000001111 写出其补码后发现有4个一这与结果是吻合的,但当我们输入负一结果就不对了显然这种方法有缺陷。我们给出改进方法:
#include<stdio.h>
int main()
{int n = 0;scanf("%d", &n);int count = 0;for (int i = 0;i < 32;i++){if (n&1==1){count++;}n = n >> i;}printf("count=%d", count);return 0;
}
这时候我们来看就会发现已经得到我们想要的结果了,按位与是不是非常的神奇呢我们来分析一下原理。
还有一个更加巧妙的方法,我们直接给出代码让你体会体会这种巧妙:
#include<stdio.h>
int main()
{int num = 0;scanf("%d", &num);int count = 0;while (num){num = num & (num - 1);count++;}printf("count=%d\n", count);return 0;
}
这段代码明显又要比上面的代码更加高效了,上面无论怎样都要执行32次循环,而下面这段代码有几个1就循环几次效率大幅提升。那为什么是这样呢?我们画个图分析一下;
2,|按位或的妙用
先来看一道题目,将13二进制序列的第5位修改为1,然后再改回0;
先画个图分析:
我们给出代码:
#include<stdio.h>
int main()
{int m = 13;int n = 5;m = m|(1 << (n - 1));printf("%d", m);return 0;
}
这里如果为什么变成了29还不知道的话那就要去看一下我之前的文章进制转换啦传送门:
那怎么换回来呢?其实也简单,我们画图来分析:
我们给出代码:
#include<stdio.h>
int main()
{int m = 13;int n = 5;m = m|(1 << (n - 1));printf("%d", m);m=m&~(1 << (n - 1));printf("%d",m);return 0;
}
由于文章篇幅有限以上就是本篇文章的所有内容啦,还有些内容将在下期发布敬请期待!
文章有点长感谢能够看到这里的读者,给你们点赞!如果这篇文章能够帮到你,制作不易还请各位读者给一个三连!你们的支持就是我最大的动力!