进制和操作符
1. 进制
我们常常会听到二进制、八进制、十进制、十六进制,而使用最多的就是十进制。这次我们就来详细介绍一下。
1.1 二进制
二进制是由 0 和 1 组合而成的,逢二进一。
个人理解为 几进制就没有几。
由于二进制是逢二进一的,所以二进制没有2。
例如,0 的二进制是 00000000,1 的二进制是 00000001,2 的二进制是00000010,3 的二进制是00000011,以此类推······
1.2 八进制
八进制是由 0 ~ 7 组成的,逢八进一,八进制也就没有八。
八进制的表示方法在开头加上 0 即可,;例如 9(十进制)的八进制为 011。
1.3 十进制
十进制是我们生活中用得最多的,比如平常计数用的都是十进制。
在前面说过 几进制就没有几,那在这里十进制为什么会出现 10 呢 ,其实从本质上来说不算是“十”,因为十进制是由 0 ~ 9 组成的,逢十进一,在 9 之后遇到了 “十” ,就需要进一位,个位上是0,十位上是 1 ,组成之后就变成了 10 。
1.4 十六进制
十六进制也是一样没有“十六”,十六进制是由数字和字母组成的。
十六进制在 9 之后就由 a ~ f 补上 ,十六进制表示的时候需在前面加上 0x 。例如,12 的十六进制是0xC ,25 的十六进制是0x19,
2. 进制转换
在使用的进制的时候,多少会碰到进制转换,现在我们就来看看进制是如何转换的。
2.1 二进制和十进制
在介绍之前,先要了解位权(权重),比如 1234 ,从右向左的权重依次是100 (个位) 、101(十位)、102(十位)、103(十位)。
二进制-------->十进制
接下来就用图片解释一下:
二进制 1011 的十进制就是 11 。
十进制-------->二进制:
十进制转换为二进制,核心是除2取余数。
2.2 二进制和八进制
八进制中最大的单位数 7 二进制只有 3 位(111),所以进制转换时取二进制三位转换为八进制的一位。具体操作如下图:
二进制-------->八进制:
90 的二进制是 0101 1010 ,从右往左每次取 3 位转换,最后不满三位也要转换,最终得到八进制:0132 。(前面的 0 不可省)
八进制-------->二进制:
八进制转换二进制,可以先将 0 后面的数拆分开来,再对拆分单位逐个进行转换,最后将转换到的二进制数字合起来就变成完整的二进制。例如,
八进制:062 八进制转换为二进制 二进制:0011 0010 。
2.3 二进制和十六进制
十六进制中最大的单位数 15 二进制只有 4 位(1111),所以进制转换时取二进制四位转换为十六进制的一位。具体操作如下图:
二进制-------->十六进制:
二进制:0001 0110 1011 二进制转换为十六进制 十六进制:0x16b
十六进制-------->二进制:
十六进制:0x12d 十六进制转换为二进制 二进制:0001 0010 1101
2.4 八进制和十进制
前面提到了位权,八进制的位权就是以 8 为底数,进行计算。
八进制-------->十进制:
八进制:066 八进制转换为十进制 十进制:54
十进制-------->八进制:
十进制:66 十进制转换为八进制 八进制:0102
2.4 八进制和十六进制
八进制和十六进制间的相互转换,可以借助二进制或者十进制作为中间值进行转换。
这里不做过多展示。
2.5 十进制和十六进制
十进制-------->十六进制:
十六进制住转换为十进制,可以借助二进制或者十进制作为中间值进行转换。
这里不做过多展示。
3. 原码、反码、补码
原码、反码、补码都是 整数 二进制位的展示方法。
这三种方法都有符号位和数值位,最高位是符号位, 0 代表正,1 代表负,除了最高位,其余位都是数值位。
注意:
原码:将数值翻译成二进制就是原码。
反码:符号位不变,数值为全部取反得到的就是反码。
补码:反码 + 1 。正整数的原码、反码、补码都相同
负数的原码、反码、补码照常计算
4. 位操作符
位操作符 | 含义 |
---|---|
& | 按位与 |
| | 按位或 |
^ | 按位异或 |
~ | 按位取反 |
<< | 左移 |
>> | 右移 |
注意:
1.位运算符运算队对象,只能是整型数据或字符型数据,不能是浮点型数据。
2.只有 ~ 为单目操作符,其他均为双目操作符。
3.运算对象一律按二进制补码形式参与运算。
4.位运算结果是整型数据。
4.1 按位与(&)
这里二进制位有 32 位,因为一个整型是 4 个字节,一个字节是八个比特位(bit)
双目运算符,只有对应的两个二进位都为 1 时,结果位才为 1 。
可以把 & 操作符理解为乘法运算。(对应二进制位相乘)
#include<stdio.h>
int main()
{
int m = 6; // 0000 0000 0000 0000 0000 0000 0000 0110
int n = 12;// 0000 0000 0000 0000 0000 0000 0000 1100
int x = 0;
x = m & n; // 0000 0000 0000 0000 0000 0000 0000 0100
printf("%d",x);
return 0;
}
运行结果:
4
4.2 按位或( | )
双目运算符,只要对应的二个二进位有一个为1时,结果位就为1。
可以把 | 操作符理解为加法运算。(对应二进制位相加(不要将两位对应的 1 相加成 2 了哦))
#include<stdio.h>
int main()
{
int m = -6; //原码: 1000 0000 0000 0000 0000 0000 0000 0110
//反码: 1111 1111 1111 1111 1111 1111 1111 1001
//补码: 1111 1111 1111 1111 1111 1111 1111 1010
int n = 12; // 0000 0000 0000 0000 0000 0000 0000 1100
int x = 0;
x = m | n; // 1111 1111 1111 1111 1111 1111 1111 1110
printf("%d",x);
return 0;
}
运行结果:
-2
4.3 按位异或( ^ )
双目操作符,相同为 0 ,相异为 1 .
可以看做为 “消消乐” ,相同就消除了(为 0) ,相异就还存在(为 1 )。
或者说对应二进制位相减之后的绝对值。
#include<stdio.h>
int main()
{
int m = -6; //原码: 1000 0000 0000 0000 0000 0000 0000 0110
//反码: 1111 1111 1111 1111 1111 1111 1111 1001
//补码: 1111 1111 1111 1111 1111 1111 1111 1010
int n = 12; // 0000 0000 0000 0000 0000 0000 0000 1100
int x = 0;
x = m ^ n; // 1111 1111 1111 1111 1111 1111 1111 0110
printf("%d",x);
return 0;
}
运行结果:
-10
4.4 按位取反( ~ )
单目操作符,将每一位二进制位取反,0 变成 1 ,1 变成 0 。
注意是每一位取反哦,不要忘了符号位。
#include<stdio.h>
int main()
{
int m = -6; //原码: 1000 0000 0000 0000 0000 0000 0000 0110
//反码: 1111 1111 1111 1111 1111 1111 1111 1001
//补码: 1111 1111 1111 1111 1111 1111 1111 1010
int x = 0;
x = ~m; // 0000 0000 0000 0000 0000 0000 0000 0101
printf("%d",x);
return 0;
}
运行结果:
5
4.5 左移操作符( << )
移位操作符也要先转换到补码再操作哦
二进制位整体向左移动,左端超过范围丢弃,右端空位补 0 ;
图解:
#include<stdio.h>
int main()
{
int m = -6; //原码: 1000 0000 0000 0000 0000 0000 0000 0110
//反码: 1111 1111 1111 1111 1111 1111 1111 1001
//补码: 1111 1111 1111 1111 1111 1111 1111 1010
int x = 0;
x = m<<1; // 1111 1111 1111 1111 1111 1111 1111 0100
printf("%d",x);
return 0;
}
运行结果:
-12
4.5 右移操作符( >> )
右移操作符有两种规则,
- 逻辑右移:右端超过范围丢弃,左端空位补 0 。
- 算术右移:右端超过范围丢弃,左端符号位不变,其余空位补 0 。
图解:(这里只演示了逻辑右移)
#include<stdio.h>
int main()
{
int m = -6; //原码: 1000 0000 0000 0000 0000 0000 0000 0110
//反码: 1111 1111 1111 1111 1111 1111 1111 1001
//补码: 1111 1111 1111 1111 1111 1111 1111 1010
int x = 0;
x = m>>1; // 1111 1111 1111 1111 1111 1111 1111 1101
printf("%d",x);
return 0;
}
运行结果:
-3
注意:
1. 编译器大多数都是算术右移。
2. 移位操作符不可移动负数位(x >> -2)。
5.优先级 、结合性
表达式运算时通常会有各种运算符参与计算,而这些操作符就决定了表达式的结果。这就涉及到优先级和结合性了。
5.1 优先级
优先级很好说明,就如以下式子:
x = 7 - 6 * 1 ;
在表达式中,既有乘号( * )又有减号( - ),根据运算符的优先级,此表达式就要先进行乘法运算,后进行减法运算。
5.2 结合性
如果出现运算符优先级相同的情况,就要看运算符的结合性,是从左往右,还是从右往左。
x = 4 * 3 / 6 ;
由于乘号( * )和除号( / )的优先级相同,根据结合性就是从左往右计算。
接下来就介绍一下最常见的运算符。
优先级 | 常见的运算符 |
---|---|
1 | 圆括号 ( ) |
2 | 自增(++)、自减(- -) |
3 | 正号(+)、负号( - ) |
4 | 乘法( * )、除法( / ) |
5 | 加法( + )、减法( - ) |
6 | 大于( > )、小于( < ) |
7 | 赋值运算符 ( = ) |
其他操作符请参考:运算符优先级
6.逗号表达式
基本格式:
(表达式 1 ,表达式 2,表达式 3,……)
计算方式是从左往右,最后一个表达式的结果就是整个表达式的结果。
既然这样,那还要前面那么多表达式干嘛呢,如果有人这样想就说明还没有完全理解。
要记住:逐个表达式从左往右依次计算!!!
例如:
#include<stdio.h>
int main()
{
int a = 5;
int b = 8;
int c = 0;
c = (a = b++, a--, b = a - 6); //逗号表达式中的值从左往右依次计算
printf("a = %d\n", a);
printf("b = %d\n", b);
printf("c = %d\n", c);
return 0;
}
运行结果:
a = 7
b = 1
c = 1
逗号操作符的优先级是最低的
最后:进制转换和位操作符可能会有些难理解,多尝试,耐心练习,会有很多收获哦 !