还记得之前对操作符进行了简单的了解,并且学习了一些常见操作符的用法,如今我们又对操作符这部分的知识进行了更深一步的学习,有了一些新的感悟和体会,这篇文章我希望能把关于操作符的方方面面,包括一些细微之处,都说到位。
话不多说,让我们开始吧!
接下来我们对这些操作符一一进行阐述!
+ - * / %
关于算数操作符,比较简单,这里说一些需要注意的:
1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除 法。
3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。
<< 左移操作符
>> 右移操作符
这是我们今天要重点介绍的操作符之一
注:他们的操作数必须是整数。
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。
下面用一张图来帮助我们理解进制
下面我们还需要了解一下原码 、反码 、补码 的知识 :
整数有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]补码
经过上面的叙述,我们很容易发现;
这里我们再举个例子看看:
而整数在内存中存储的是补码!!!
而VS编译器是16进制展示
所以显示的内存是 f f f f f f f f
我们知道16进制的 f 就是10进制的15,所以这里对应的就是-1的补码
首先需要说明的是:
移位操作符操作的是补码
而打印或使用的时候,用的是补码
所以使用移位操作符会某个数的二进制的补码改变后,我们如果需要打印或使用这个数的二进制是,要先通过改变后的补码反推出改变后的原码
铺垫做的差不多了,下面正式介绍移位操作符 !
(1).左移操作符
移位规则:左边抛弃、右边补0
如图,通过左移操作符把num向左移了一位, 正数10的二进制的补码 左边抛弃、右边补0
变成了 00000000000000000000000000010100
计算结果就为 1*2^4+1*2^2=20 了
如果要打印
int num2=num<<1;
printf("%d",num2);
这里的打印用的是num2的原码的值
需要注意的是:
这时num的值还是10,没有改变,只是我们计算了一下 num<<1 的结果而已
(2).右移操作符
移位规则:
首先右移运算分两种:
到底是哪种取决于编译器,我们常见的编译器下都是算数右移
如果是正数,这两种结果一样
我们通过一张图来展示一下右移操作符的作用效果
我们用一个负数 -5 来看一下
经过算术操作符,结果是 -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 ;
我们用一张图表示运算的过程:
(2). | 按位或
int a = 3 ;
int b = -5 ;
int c = a | b ;
结果是 -5
(3). ^ 按位异或
int a = 3 ;
int b = -5 ;
int c = a ^ b ;
结果是 -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 (真或假)