1.操作符的分类
- 算术操作符:+、-、*、/、%
- 移位操作符:<< >> (移动的是二进制位)
- 位操作符:& | ^ (也是使用二进制位进行计算)
- 赋值操作符:=、+=、-=、*=、/=、%=、<<=、>>=、&=、|=、^=
- 单目操作符:!、++、--、&、*、+、-、~、sizeof、(类型)
- 关系操作符:>、>=、<、<=、==、!=
- 逻辑操作符:&&、||
- 条件操作符:? :
- 逗号表达式:,
- 下标引用:[ ]
- 函数调用:()
- 结构成员访问:. 、->
2.原码、反码、补码
整数的二进制表示方法有3种
有符号整数的三种表示方法均有符号位和数值位两部分,二进制序列中,最高位的一位当作符号位,其余都是数值位。符号位中:0表示正,1表示负
正整数的原、反、补码都相同
负整数的三种表示方法各不相同
原码:直接将数值按照正负数的形式翻成二进制
反码:将原码的符号位不变,其他位依次按位取反
补码:反码+1
补码原码相互转化:取反+1
int b=10;
//0000000000000000000000000001010--原码
//0000000000000000000000000001010--反码
//0000000000000000000000000001010--补码
int a=-10;
//1000000000000000000000000001010--原码
//1111111111111111111111111110101--反码
//1111111111111111111111111110110--补码
对于整数来说:整数在内存中存放的就是其二进制的补码
why?在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程都是相同的,不需要额外硬件电路。
3.移位操作符
<<左移位操作符
>>右移位操作符 注:移位操作符的操作数只能是整数
3.1左移操作符 <<
左边舍弃,右边补0
- 左移一位有乘二的效果哦
int a=10;
int b=a<<1;//a左移一位--->移动的是二进制位
//10的二进制1010,a占4个字节
//00000000000000000000000000001010
//00000000000000000000000000010100
printf("b=%d\n",b);//20
printf("a=%d\n",a);//10
//若相让a变化,则写a <<= 1
注意:存储形式是补码,要先转化为补码形式再左移,还要换成原码,显示出来的是原码十进制
int a = -1;
int b = a << 1;
//100000000000000000000000000000001--a的原码
//111111111111111111111111111111110
//111111111111111111111111111111111--补码
//左移一位
//111111111111111111111111111111110--b的补码
//100000000000000000000000000000001
//100000000000000000000000000000010--b的原码
printf("b=%d\n",b);//-2
printf("a=%d\n",a);//-1
3.2右移操作符 >>
- 逻辑右移:左边补0,右边舍弃
- 算术右移:左边该值的符号位填充,右边舍弃
右移到底采用哪种方式取决于编译器,通常采用算术右移。有除二的效果
int a = -10;
//1000000000000000000001010--a原码
//1111111111111111111110101
//1111111111111111111110110--补码
int b = a >> 1;//算术右移
//1111111111111111111111011--b补码
//1000000000000000000000100
//1000000000000000000000101--b原码
printf("b=%d\n",b);//-5
printf("a=%d\n",a);//-10
4.位操作符:&、|、^、~
- & //按(二进制)位与 有0为0,同为1则1
- | //按(二进制)位或 只要有1则为1,同时为0才0
- ^ //按(二进制)位异或 相同为0,相异为1
- ~ //按(二进制)位取反 取反得到补码
注意:他们的操作数必须是整数
int a = 6;
//0000000000000000000000000000000110---6的补码(正数原反补相同)
//
int b = -7;
//1000000000000000000000000000000111
//1111111111111111111111111111111000
//1111111111111111111111111111111001--->-7的补码
int c = a & b;//a和b的补码的二进制位进行运算
//对于二进制位补码,有0则为0,两个同时为1,才为1
//0000000000000000000000000000000110---> 6的补码
//1111111111111111111111111111111001--->-7的补码
//0000000000000000000000000000000000
//c = 0
int c = a | b;//规则:只要有1,就是1,两个同时为0,才为0
//0000000000000000000000000000000110---> 6的补码
//1111111111111111111111111111111001--->-7的补码
//1111111111111111111111111111111111--->c的补码
//1000000000000000000000000000000001--->c的原码
//c = -1
int c = a ^ b;//按位异或:相同为0,相异为1
//0000000000000000000000000000000110---> 6的补码
//1111111111111111111111111111111001--->-7的补码
//1111111111111111111111111111111111
//1000000000000000000000000000000001--->原码
//c = -1
int a = 0;
printf("%d",~a);
//~ 按位取反
//00000000000000000000000000000000 -- 0的补码
//11111111111111111111111111111111 -- 取反 (~a补码)
//00000000000000000000000000000001 -- 原码
~a = -1
4.1操作符的特点
^ 支持交换律
3 ^ 5 ^ 3 = 5
3 ^ 3 ^ 5 = 5
//不能创建第三个变量实现两数交换
int main()
{
int a = 3;
int b = 5;
a = a ^ b;
b = a ^ b;
a = a ^ b; //a^a^b=b
}
//实现交换
0 ^ a = a
a ^ a = 0
4.2练习题
练习1:求一个整数存储在内存中的二进制的1的个数
#include<stdio.h>
int count_bit_1(unsigned int n)
{
int count=0;
while(n) //把n转化为二进制,求其中1的个数,除基取余
{
if((n%2) == 1)
count ++;
n = n / 2;//得到商
}
return count;
}
int main()
{
int num=0;
scanf("%d",&num);
int ret = count_bit_1(num);//传入无符号整型
printf("%d\n",ret);
}
//方法二
int count_bit_1(int n)
{
int count=0;
for(int i=0;i<32;i++)
{
if(((n>>i) & 1) == 1)
{
count ++;
}
}
return count;
}
缺点:会把每一位都算一遍
//方法三
int count_bit_1(int n)
{
int i= 0;
int count=0;
while(n) //有几个1循环几次
{
n=n&(n-1);
count ++;
}
return count;
}
n & 1 == 1 //说明n最后一位是1
n-1 //把n的二进制序列中的最右边的1去掉了
n = n & (n-1) 逐次去掉一个1
【例】n = 1011
n-1 = 1010
n = 1010
n-1 = 1001
n = 1000
练习2:判断n是否是2的次方数
思路:2的次方数的二进制数中只有一个1
if(n&(n-1) == 0)
{
cout<<"yes";
}