操作符详解(上)
1.操作符的分类
- 算术操作符:+, -,*,%,/,
- 移位操作符:<< >> 移动的是二进制位
- 位操作符:&,|,^, 也是使用二进制位进行计算
- 赋值操作符:=,+=,-=,*=,/=,%=,<<=,>>=,&=,|=,^=,
- 单目操作符:!,++,--,&,*,+,-,~,sizeof,
- 关系操作符:>,>=,<,<=,==,!=,
- 逻辑操作符:&&,||,
- 条件操作符:?:
- 逗号表达式:,
- 下标引用:[ ]
- 函数调用:()
- 结构成员访问:.,->
上述的操作符,我们已经讲过算术操作符,赋值操作符,逻辑操作符,条件操作符和部分的单目操作符,今天继续介绍一部分,操作符中有一些操作符和二进制有关系,我们先铺垫一下二进制的和进制转换的知识。
2.二进制和进制转换
其实我们经常能听到2进制,8进制,16进制这样的讲法,那是什么意思呢?
其实2进制,8进制,16进制是数值的不同表示形式而已。
比如:数值15的各种进制的表示形式:
15的2进制:111115的8进制:1715的10进制:1515的16进制:F//16进制的数值之前写:0x
//8进制的数值之前写:0
我们重点介绍一下二进制:
首先我们还是得从10进制讲起,10进制是我们生活中经常使用的,我们已经形成了很多尝试:
- 10进制中满10进1
- 10进制的数字每一位都是0~9的数字组成
其实2进制也是一样的
- 2进制中满2进1
- 2进制的数字每一位都是0~1的数字组成
那么1101就是2进制的数字了。
2.1 2进制转10进制
其实10进制的123表示的值是一百二十三,为什么是这个值呢?其实10进制的每一位都是有权重的,10进制从右向左是个位,十位,百位……,分别每一位的权重是10^0,10^1,10^2...
2进制和10进制是类似的,只不过2进制的每一位的权重,从右向左是:2^0,2^1,2^2....
如果2进制的1101,该怎么理解呢?
1*2^3+1*2^2+0*2^1+ 1*2^0 = 1*8+1*4+0+1 = 13
2.1.1 10进制转2进制数字
2.2 2进制转8进制和16进制
2.2.1 2进制转8进制
8进制的数字每一位是0~7的,0~7的数字,各自写成2进制,最多有3个2进制为就足够了,比如7的二进制是111,所以在2进制转8进制的时候,从2进制序列中右边低位开始向左每3个2进制位会换算一个8进制,剩余不够3个2进制位的直接换算。
如:2进制的01101011,换成8进制:0153,0开头的数字,会被当做8进制
2.2.2 2进制转16进制
16进制的数字每一位是0~9,a~f的,0~9,a~f的数字,各自写成2进制,最多有4个2进制位就足够了,比如f的2进制是1111,所以在2进制转16进制数的时候,从2进制序列中右边低位开始向左每4个2进制位会换算一个16进制位,剩余不够4个二进制位的直接换算。
如2进制的01101011,换成16进制
3.原码,反码,补码
整数的2进制表示方法有三种,则原码,反码和补码
有符号整数的三种表示方法均有符号位和数值位两部分,2进制序列中,最高位的1位是被当做符号位,剩余的都是数值位。
符号位都是用0表示正,1表示负。
正整数的原,反,补码都相同
负整数的三种表示方法各不相同
原码:直接将数值位按照正负数的形式翻译成二进制得到的就是原码。
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码:反码+1就得到补码
补码得到原码也是可以使用:取反,+1的操作。
对于整形来说:数据存放内存中其实存放的是补码。
为什么呢?
在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码和原码可以相互转换,其实运算过程是相同的,不需要额外的硬件电路。
4.移位操作符
<< 左移操作符
>> 右移操作符
注:移位操作符的操作数只能是整数。
移动的是存储在内存中的二进制位(补码)
4.1 左移操作符
移位规则:左边抛弃,右边补0
int main()
{int num = 10;int n = num << 1;printf("n=%d\n", n);printf("num = %d\n", num);return 0;
}
10的原码 000000001010 左移之后变成 000000010100 换算成10进制为20
代码结果为
4.2 右移操作符
移位规则:首先右移运算分两种
1.逻辑右移:左边用0填充,右边丢弃
2.算术右移:左边用该原值的符号位填充,右边丢弃
右移到底采用算术右移还是逻辑右移是取决于编译器的! 通常采用的都是算术右移
注意:对于移位运算符,不要移动负数位,这个是标准未定义的。
例如:
int num = 10;
num >> -1; // error
5.位操作符:&,|,^,~
位操作符有:
& //按位与 与&&区分 && 逻辑与
| //按位或 与 | |区分 | | 逻辑或
^ //按位异或
~ //按位取反
注意: 他们的操作数必须是整数。
直接上代码:
按位与:&
int main()
{int a = 6;// 00000000000000000000000000000110 6的补码int b = -7;// 10000000000000000000000000000111// 11111111111111111111111111111000// 11111111111111111111111111111001 7的补码int c = a & b; // a和b的补码的二进制位进行按位与运算// 对应的二进制位 有0则为0 两个同时为1 才为1// 00000000000000000000000000000110 6的补码// 11111111111111111111111111111001 7的补码// 00000000000000000000000000000000 结果为0printf("%d\n", c);return 0;
}
按位或:|
int main()
{int a = 6;// 00000000000000000000000000000110 6的补码int b = -7;// 10000000000000000000000000000111// 11111111111111111111111111111000// 11111111111111111111111111111001 7的补码int c = a | b; // a和b的补码的二进制位进行按位或运算// 只要有1,就为1,两个同时为0,才为0// 00000000000000000000000000000110 6的补码// 11111111111111111111111111111001 7的补码// 11111111111111111111111111111111 // 10000000000000000000000000000000// 10000000000000000000000000000001 原码 结果为-1printf("%d\n", c);return 0;
}
按位异或:^
int main()
{int a = 6;// 00000000000000000000000000000110 6的补码int b = -7;// 10000000000000000000000000000111// 11111111111111111111111111111000// 11111111111111111111111111111001 7的补码int c = a | b; // a和b的补码的二进制位进行按位异或运算// 对应的二进制位 相同为0 相异为1// 00000000000000000000000000000110 6的补码// 11111111111111111111111111111001 7的补码// 11111111111111111111111111111111 // 10000000000000000000000000000000// 10000000000000000000000000000001 原码 结果为-1printf("%d\n", c);return 0;
}
按位取反 : ^
注意: 它和按位与 ,按位或,按位异或不同 前面三个都是双目操作符 它是单目操作符
int main()
{int a = 0;printf("%d\n", ~a);// 按二进制位取反// 00000000000000000000000000000000 0的补码// 11111111111111111111111111111111// 10000000000000000000000000000000// 10000000000000000000000000000001 取反之后的原码 结果为-1return 0;
}
一道变态的面试题:
不能创建临时变量(第三个变量 ),实现两个数的交换。
^操作符的特点^对应的二进制位 相同为0 相异为1所以 3 ^ 3 = 0a ^ a = 0那么 0 ^ 3 = 30 ^ a = a
所以可以推出 3^3^5 = 53^5^3 = 5int main()
{int a = 3;int b = 5;printf("交换前:a=%d b=%d\n", a, b);a = a ^ b;b = a ^ b;a = a ^ b;printf("交换后:a=%d b=%d\n", a, b);return 0;}
代码结果如下:
练习1:编写代码实现:求一个整数存储在内存中二进制中1的个数。
int count_bit_one(unsigned int n)
{int count = 0;while (n){if ((n % 2) == 1)count++;n = n / 2;}return count;
}int main()
{int num = 0;scanf("%d", &num);int ret = count_bit_one(num);printf("%d\n", ret);return 0;
}
还有另外一种方法:
int count_bit_one(int n)
{int i = 0;int count = 0;for (i = 0; i < 32; i++){if (((n >> i) & 1) == 1){count++;}}return count;
}int main()
{int num = 0;scanf("%d", &num);int ret = count_bit_one(num);printf("%d\n", ret);return 0;
}
不管二进制中有多少个1,都得算32次,所以时间复杂度高。
进阶版 (二进制中有多少个1,就算多少次):
//进阶版
//n = n & (n - 1)
/*假设n = 1011 所以n = 1010n = 1011n-1 = 1010n = 1010
n-1 = 1001
n = 1000
n-1 = 0111
n = 0000
所以我们可以知道n = n & (n - 1)的作用就是去掉二进制位中最低位的1
*/int count_bit_one(int n)
{int i = 0;int count = 0;while (n){n = n & (n - 1);count++;}return count;
}int main()
{int num = 0;scanf("%d", &num);int ret = count_bit_one(num);printf("%d\n", ret);return 0;
}
练习2:二进制位置0或者置1
编写代码将13二进制序列的第5位修改为1,然后再改为0
13的2进制序列: 00000000000000000000000000001101
将第5位置为1后:00000000000000000000000000011101
将第5位置为0后:00000000000000000000000000001101
int main()
{int a = 13;
//将13二进制序列的第5位修改为1
// n
// 13的2进制序列: 00000000000000000000000000001101
// 1<<(n-1) : 00000000000000000000000000010000
// a = a | (1 << (n - 1)): 00000000000000000000000000011101int n = 5;a = a | (1 << (n - 1));printf("%d\n", a);
// 再改为0
// 13的2进制序列:00000000000000000000000000011101
// ~( 1<<(n-1)) :11111111111111111111111111101111
// a & ~(1 << (n - 1)) :00000000000000000000000000001101a &= ~(a << (n - 1));return 0;
}
以上就是对操作符详解(上)的详细介绍 制作不易 喜欢的朋友记得点赞评论收藏哦!!!