C语言操作符详解
原码 反码 补码
- 原码就是十进制数值转换为二进制对应的值,如果为负数,则最高位为1,否则为0.
13--->00001101(原码)
-10--->10001010(原码)
- 反码,正数的反码为其原码,复数的反码则是最高位不变,其他位则取反。
13--->00001101--->00001101(反码)
-10--->10001010--->11110101(反码)
- 补码,正数的补码为其原码,而负数的补码则是在其反码最低位加1
13--->00001101--->00001101--->00001101(补码)
-10--->10001010--->11110101--->11110110(补码)
在计算机内,存储器存储的是补码,计算机进行运算时也是对补码进行加减,同时,操作符对数值进行操作时也是对补码进行操作,并且操作符不会改变原数值,只会产生一个新的结果,而当计算机要输出一个值时,输出的是其原码对应的值,printf打印出的结果也就是原码转换为后的值
补码变原码则是补码取反加一
for example
a=-7;
a对应的补码是111111111111111111111111111111001
算其原码
先取反:100000000000000000000000000000110
再加一:100000000000000000000000000000111
printf时,若是%d,则把100000000000000000000000000000111转换为10进制即可
否则,若是111111111111111111111111111111001,结果一定不是-7
位移操作符
一、左移操作符
<< n
将数值的补码进行向左移动n位,同时每移动一位,右边补0.
int n=1;
int b=0;
b=n<<1;//00000001--->00000010,b=2
二、右移操作符
n>>
将数值向右移动n位,但是左边补位分两种情况
1. 逻辑右移:左边补0.
11111111--->01111111(向右移动一位)
2. 算术右移:左边补它的符号位
11110101--->11111010(向右移动一位)
01100101--->00110010(向右移动一位)
# include<stdio.h>
int main()
{
int a = -6;
a = 1 >> a;
printf("%d\n", a);
return 0
}
但是究竟是算术右移还是逻辑右移取决于编译器,大部分编译器都是算术右移
一般来说右移相当于除2的效果,而左移相当于乘2的效果
位操作符
一、 按位与&
两个数的的补码按位与时,同为1为1,不同为1时为0,与&&相同
a=6
b=-1
a&b
000000000000000000000000000000110
111111111111111111111111111111111
000000000000000000000000000000110
# include<stdio.h>
int main()
{
int a = 6;
int b = -1;
int c = a&b;
printf("%d\n", c);
return 0;
}
二 、按位或 |
两个数的的补码按位与时,有为1为1,都为0时为0,与||相同
a=6
b=-1
a|b
000000000000000000000000000000110
111111111111111111111111111111111
111111111111111111111111111111111
# include<stdio.h>
int main()
{
int a = 6;
int b = -1;
int c = a|b;
printf("%d\n", c);
return 0;
}
三、按位异或 ^
相同为0相异为1.
a=6
b=-1
a^b
000000000000000000000000000000110
111111111111111111111111111111111
111111111111111111111111111111001
# include<stdio.h>
int main()
{
int a = 6;
int b = -1;
int c = a^b;
printf("%d\n", c);
return 0;
}
四按位取反 ~
简单来说就是1变0,0变1
a=6
a:000000000000000000000000000000110
~a:111111111111111111111111111111001
1. 对于& | ~,这三个操作符,对于初学者学习单片机是非常重要的,比如说在在控制LED灯时就要用到
在这里我举一个实例
# include <REGX52.H>
void main()
{
P3=0xFF;
P2=1//让引脚接P2的LED灯全灭
while(1)
{
if P3_1==0
{
Delay(20);//按键消抖
while(P3_1);
Delay(20);//按键消抖,同时这个函数需要自己去创建
P2_0=~P2_0;//这样当按下再松开时,就可以点亮一个灯
}
}
}
当然这里写的比较简洁,我们再看一个应用,如何做到不用创建第三个变量来交换两个变量的值
首先,a^0仍然为a原来的值
a^a=0;
int a = 6;
int b = -1;
a=a^b;
b=a^b;//实际上是a^b^b--->a^0--->a
a=a^b;//a^a^b--->0^b--->b
2. 这里我们再看一个应用,统计一个数的补码的1的个数
# include<stdio.h>
int main()
{
int n = 0, count = 0;
printf("请输入一个值\n");
scanf_s("%d", &n);//输入我们要统计的数
int i = 0;
for (i = 0; i < 32; i++)
{7
if (((n >> i) & 1 )== 1)
count++;
}
printf("%d中1的个数时%d\n", n, count);
return 0;
}
3. 接下来我们看如何判断一个数是否为为2的次方
# include<stdio.h>
int main()
{
int n=0;
scanf("%d",&n);
if(n&(n-1)==0) printf("是偶数\n");
return 0;
}
这是为什么呢,我们来分析一下
1. 首先2的次方那么它的二进制一定是只有一位是1其余为都是0,
呐有没有可能会有2位及以上是1呢,
当然不可能因为2的x次方与2的x+k次方相加结果是2^x(1+2^k),
三个呢也肯定是偶数与奇数相加(大家可以试着去推一下,还有一件事这里^是次方的意思不是按位异或)
2. 所以说如果说n为2的次方的话,减1后一定是·····01111······1的结果,如果&n的话,结果就必然是0
##五 整型提升
首先我们来看一个情况
char a=3;
char b=127;
char c=a+b;
printf("%d ",c);
那么答案应该是多少呢?是130吗?,当然不是,因为这是字符型变量,130明显超过了char的范围,而且是在整型相加。
由于CPU的整形运算器操作数的字节数长度一般为int型,所以,当进行整形运算,但是,数据类型是缺省整型类型的精度来进行的。那么为了获得整形精度就要进行整型提升(注意接下来的二进制码都为补码)
我们来一步一步看与分析
a=3:00000011
提升后a=000000000000000000000000000000011
b=127:01111111
提升后b=000000000000000000000000011111111
000000000000000000000000000000011
000000000000000000000000001111111
000000000000000000000000010000010
由于char只取8位所以a+b的结果是10000010
而对于printf("%d ",c);
%d是10进制整型格式,所以要把00000010,再次整型提升位int型
111111111111111111111111110000010
得到其原码
100000000000000000000000001111110