超详细的操作符解析

  还记得之前对操作符进行了简单的了解,并且学习了一些常见操作符的用法,如今我们又对操作符这部分的知识进行了更深一步的学习,有了一些新的感悟和体会,这篇文章我希望能把关于操作符的方方面面,包括一些细微之处,都说到位。

  话不多说,让我们开始吧!

一.操作符分类

  • 操作符有哪些?可以分为几类:
  • 算术操作符
  • 移位操作符
  • 位操作符
  • 赋值操作符
  • 单目操作符
  • 关系操作符
  • 逻辑操作符
  • 条件操作符
  • 逗号表达式
  • 下标引用、函数调用和结构成员 

接下来我们对这些操作符一一进行阐述! 

二.算术操作符

+            -            *            /            % 

关于算数操作符,比较简单,这里说一些需要注意的:

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

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

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

三.移位操作符 

<<     左移操作符

>>     右移操作符

这是我们今天要重点介绍的操作符之一 

注:他们的操作数必须是整数。

1.有关进制和原码、反码、补码


int a = 5;
int b = a << 2;

这里的 a<<2 是把a向左移动2位,实质是:把a在内存中存储的二进制位向左移动两位

这里补充一下进制的知识:

  • 二进制:逢二进一

       基数为2,数值部分用两个不同的数字0、1来表示。

  • 十进制:逢十进一

       基数为10,数值部分用0、1、2、3、4、5、6、7、8、9来表示.

  • 十六进制:逢十六进一

       基数是16,有十六种数字符号,除了在十进制中的0至9外,还另外用6个英文字母A、B、C、     D、E、F来表示十进制数的10至15。

下面用一张图来帮助我们理解进制 

 

超详细的操作符解析_第1张图片

下面我们还需要了解一下原码 、反码 、补码  的知识 :

整数有3种二进制表示形式:

  • 原码
  • 反码
  • 补码

  (1).原码 

原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制

[+1]原码 = 0000 0001

[-1] 原码 = 1000 0001

  (2).反码 

反码的表示方法是:

正数的反码是其本身

负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.

[+1]   =   [00000001]原码  =  [00000001]反码

[- 1]   =   [10000001]原码  =  [111111110]反码

  (3).补码 

补码的表示方法是:

正数的补码就是其本身

负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)

[+1]   =   [00000001]原   =   [00000001]反码  =   [00000001]补码

[-1]    =   [10000001]原   =   [11111110] 反码   =   [11111111]补码

经过上面的叙述,我们很容易发现;

  • 正整数:原码、补码、反码相同
  • 负整数:原码、补码、反码不同,需要计算

这里我们再举个例子看看:

超详细的操作符解析_第2张图片

而整数在内存中存储的是补码!!! 

超详细的操作符解析_第3张图片 如图,-1的在内存中存储的是补码

而VS编译器是16进制展示

所以显示的内存是 f f   f f   f f   f f 

 我们知道16进制的 f 就是10进制的15,所以这里对应的就是-1的补码

 

2.移位操作符 

首先需要说明的是:

移位操作符操作的是补码

而打印或使用的时候,用的是补码

     所以使用移位操作符会某个数的二进制的补码改变后,我们如果需要打印或使用这个数的二进制是,要先通过改变后的补码反推出改变后的原码 

铺垫做的差不多了,下面正式介绍移位操作符 !

(1).左移操作符

移位规则:左边抛弃、右边补0

超详细的操作符解析_第4张图片 

如图,通过左移操作符把num向左移了一位, 正数10的二进制的补码 左边抛弃、右边补0

 变成了 00000000000000000000000000010100

计算结果就为 1*2^4+1*2^2=20 了

 如果要打印

 int num2=num<<1;
 printf("%d",num2);

这里的打印用的是num2的原码的值

需要注意的是:

这时num的值还是10,没有改变,只是我们计算了一下 num<<1 的结果而已

(2).右移操作符 

移位规则:

首先右移运算分两种:

  1. 算术移位 :  左边用原该值的符号位填充,右边丢弃
  2. 逻辑移位  :左边用0填充,右边丢弃

到底是哪种取决于编译器,我们常见的编译器下都是算数右移

如果是正数,这两种结果一样

我们通过一张图来展示一下右移操作符的作用效果 

超详细的操作符解析_第5张图片 

 我们用一个负数 -5 来看一下

超详细的操作符解析_第6张图片 

经过算术操作符,结果是 -3

经过逻辑操作符,结果是  3 

警告⚠ :

对于移位运算符,不要移动负数位,这个是标准未定义的行为 

int b = a >> -2;

这是不行的!

四.位操作符 

位操作符有:

&         //按位与

        //按位或

       //按位异或

注:他们的操作数必须是整数。 

这里的”位“指的都是二进制位 

& 按位与 (对应的二进制位有0则为0,全1才为1)

按位或  (有1为1,全0为0)

按位异或 (相同为0,相异为1)

(1).&  按位与 

 我们举个例子


int a = 3 ;
int b = -5 ;
int c = a & b ;

我们用一张图表示运算的过程: 

超详细的操作符解析_第7张图片 

 (2). |  按位或

int a = 3 ;
int b = -5 ;
int c = a | b ;

 超详细的操作符解析_第8张图片

 

结果是 -5 

(3). ^  按位异或 

int a = 3 ;
int b = -5 ;
int c = a ^ b ;

超详细的操作符解析_第9张图片 

 

结果是 -8

下面我们看曾经一道变态的面试题 

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

首先我们应该明确:

                 a ^ a = 0

                 a ^ 0 = a 

上代码! 

#include 
int main()
{
 int a = 10;
 int b = 20;
 a = a^b;
 b = a^b;
 a = a^b;
 printf("a = %d b = %d\n", a, b);
 return 0;
}

一个练习:

编写代码实现:求一个整数存储在内存中的二进制中1的个数。

//方法1
#include 
int main()
{
 int num  = 10;
 int count=  0;//计数
 while(num)
 {
 if(num%2 == 1)
 count++;
 num = num/2;
 }
 printf("二进制中1的个数 = %d\n", count);
 return 0;
}
//思考这样的实现方式有没有问题?
//方法2:
#include 
int main()
{
 int num = -1;
 int i = 0;
 int count = 0;//计数
 for(i=0; i<32; i++)
 {
 if( num & (1 << i) )
 count++; 
 }
 printf("二进制中1的个数 = %d\n",count);
 return 0;
}
//思考还能不能更加优化,这里必须循环32次的。
//方法3:
#include 
int main()
{
 int num = -1;
 int i = 0;
 int count = 0;//计数
 while(num)
 {
 count++;
 num = num&(num-1);
 }
 printf("二进制中1的个数 = %d\n",count);
 return 0;
}
//这种方式是不是很好?达到了优化的效果,但是难以想到。

五.赋值操作符 

            赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋值。 

 int weight = 120;  //体重

weight = 89;  //不满意就赋值

double salary = 10000.0;

salary = 20000.0;  //使用赋值操作符赋值。 

赋值操作符可以连续使用,比如: 

int a = 10;
int x = 0;
int y = 20;
a = x = y+1;//连续赋值

 a的结果是21,这样写是没错,但不建议这样写

复合操作符 

+=

-=

*=

/=

%=

>>=

<<=

&=

|=

^= 

这些运算符都可以写成复合的效果。 比如:

int x = 10;
x = x+10;
x += 10;//复合赋值
//其他运算符一样的道理。这样写更加简洁。

六.单目操作符 

!           逻辑反操作

-           负值

+           正值

&           取地址

sizeof      操作数的类型长度(以字节为单位)

~             对一个数的二进制按位取反

--      前置、后置--

++    前置、后置++

*           间接访问操作符(解引用操作符)

(类型)       强制类型转换 

 

(1).逻辑反操作符 !

如:

int flag = 0;

 则  !flag = 1

 若

int flag = 5;

!flag = 0 

注:!flag的结果只能为1或0  (真或假)  

 

 

 

 

 

 

 

 

你可能感兴趣的:(c++,c语言)