一.二进制和进制转换
(一)进制的说明
2进制,8进制,10进制,16进制只是数值的不同表示形式而已。
比如:数值15的各种进制表示形式
2进制: 1111
8进制: 17
10进制:15
16进制:F
他们表示满几进1,比如8进制中表示满8进1,8进制中的每一位数字都由0~7构成。
16进制中的每一位数字都由0~15构成,但为了不被误会,所以将10~15的数字在16进制中分别表示成a b c d e f,即:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0 1 2 3 4 5 6 7 8 9 a b c d e f
(二)进制之间的相互转换
1.将各种进制转换为十进制:求权重
进制中的每一位数字都有各自对应的权重
比如:10进制中的123:
百位 十位 个位
权重 10^2 10^1 10^0
求值 1*100 + 2*10 + 3*1 = 123
2进制也是如此,比如1101
1 1 0 1
权重 2^3 2^2 2^1 2^0
求值 1*8 + 1*4 + 0*2 + 1*1 = 13
2.将十进制转换各种进制:算除法,求余数 (要算成几进制,就除以几)
运用除法求余数的方法,比如:将十进制转换为二进制:
因此,要背会二进制的权重,这样能更快的转换为与二进制相关的
1 1 1 1 1 1 1
二进制中的权重 64 32 16 8 4 2 1
比如:求15(十进制)的二进制,所以即为 1 1 1 1
求14的(十进制)二进制,所以即为 1 1 1 0
求30的(十进制)二进制,所以即为 1 1 1 1 0
3.各种进制转换各种进制(实质上还是2进制与10进制之间的转换)
(1)2进制转换为8进制
将二进制的数字从右往左以三个为一组来取,每三个求一个十进制的数字,不够三个的就按那几个求就行,最后算出的数字组合起来就是八进制。
例如:将二进制的01101011转化为八进制
注意:0开头的数字,会被当做八进制,因此,这个八进制为0153.
(2)8进制转换为2进制
同理,若将8进制转换为2进制,就需要将八进制的每一位数字先看成十进制,将他们依次转换为三个二进制数字。
需注意:转换后的二进制数字最前面的0要去掉。
(3)2进制转换为16进制
将二进制的数字从右往左以四个为一组来取,每四个求一个十进制的数字,不够四个的就按那几个求就行,最后算出的数字组合起来就是十六进制。
例如:将二进制的01101011转换为十六进制
注意:16进制表示的时候前面要加0x,因此,这个16进制为0x6b
(4)16进制转换为2进制
同理,若将16进制转换为2进制,就需要将16进制的每一位数字先看成十进制,将他们依次转换为四个2进制数字。
需注意:转换后的二进制数字最前面的0要去掉。
二.原码,反码,补码
数据是以二进制的形式表示和存储的
整型——二进制形式(原码,反码,补码)
浮点型——二进制形式(浮点数的存储方式)
整数的2进制表示方法由三种,即原码,反码和补码
整数分为有符号整数和无符号整数。
有符号整数的三种表示方法均有符号位和数值位,2进制序列中,最高位的一位是被当作符号位,剩余的都是数值位。符号位都是用0表示“正”(严谨来说是非负),用1表示“负”。(口诀:0正1负)
无符号整数的三种表示方法只有数值位,没有符号位,
但他们都是占32个比特位
有符号整数
无符号整数
非负整数(它的原码,反码,补码都一样)(0也一样)
有符号整数{
负整数(它的三种表示方法各不相同)
整数{
无符号整数(它的原码,反码,补码都一样)
负整数的原码,反码,补码
原码:直接将数值按照正负数的形式翻译成二进制得到的就是原码
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码
补码:反码+1就可以得到补码
原码得到补码也是可以使用:取反,+1的操作
补码得到原码也是可以使用:取反,+1的操作
例子:
三.操作符的分类
1.算数操作符:+,-,*,/,%
2.移位操作符:<< >> (只能针对整型的二进制进行操作)(计算机用补码进行移位计算)
<<:左移操作符(有*2的效果,前提是移动不能让最前面最先出现的数值“1”移出去,不然丢弃后会影响很大,*2效果微乎其微) :左边抛弃,右边补0
注:将num移动一位后的结果赋值给了r,实际上num的值是不变的。
>>:右移操作符 (在算数右移的情况下有/2的效果):分为逻辑右移和算数右移
(1)逻辑右移:左边用0填充,右边丢弃
(2)算数右移:左边空缺的地方全都用原来该值的符号位填充,右边丢弃
如果是正数:左边通通补0
如果是负数:左边通通补1
C语言标准没有给出明确的规定,右移到底是采用哪种方式,最终还是取决于编译器的实现。
注意:计算机是用补码进行计算的,人是用原码计算的!因此要推测计算的的结果,要将原码变成补码进行计算!!!最后计算机打印(prinf)出来的结果是原码,所以最后还要将补码换成原码。
即计算时要先求出原码,再将它变成补码,然后将补码经过变化后再变成原码,就是它最终的值!!!每次进行这三个变换前,都要先看看它们是不是正数,如果是正数,原,反,补就都一样了(0也一样),就别再变换了,要不就错了
3.位操作符:& | ^ ~ (只能针对整型的二进制进行操作)
&(看0):按位与 :所对应的补码只要有一个为0则为0,两个都为1才能为1
num假设是一个随机的数字
num & 1 就可以得到num的二进制中最低位的值
比如:num & 1 == 1 ,就说明num的最低位是1
num & 1 == 0 ,就说明num的最低位是0
例子:
|(看1): 按位或 :所对应的补码只要有一个为1则为1,两个都为0才能为0
例子:
^:按位异或:所对应的补码相同为0,相异为1
异或是支持交换律的;任何数与0异或是它本身;两个相同的数异或是0;
例如:a^0=a a^a=0
3^5^3=3^3^5=5
例子:
~:按位取反: 将所有补码都取反,符号位也取反
例子:
按位取反还可以交换两数的值,在不利用第三个数的情况下:
练习题1:如何打印出一个数字中的二进制位中有几个“1”?
方法一:
方法二:
有一个公式,若有一个数为n,则通过公式:n&(n-1),每进行一次这个公式,就可以将n的二进制中的1去掉一次
练习题2:请输入一个数,判断它是不是二的次方数
练习题3:编写代码,将13的二进制序列的第5位修改成1,然后再改回0
4.赋值操作符:=,+=,-=,*=,/=,%=,<<=,>>=,&=,|=,^=
5.单目操作符:!,++,--,&,*,+,-,~,sizeof
对于sizeof(1.当括号中有表达式的时候,是不参与计算的 2.它是计算数据类型的长度的,而非数值 3.打印 sizeof 时要用%zd打印),例如:
6.关系操作符:>,>=,<,<=,==,!=
7.逻辑操作符:&&,||(表示逻辑或)
8.条件操作符:?:
9.逗号表达式:,
逗号表达式,就是用逗号隔开的多个表达式
逗号表达式,从左往右依次执行。整个表达式的结果是最后一个表达式的结果。
例如:
10.下标引用:[ ]
11.函数调用:()
12.结构成员访问:. ,->
四.操作符的属性:优先级,结合性
几个常用的优先级
五.表达式求值:
(一).整型提升(针对所计算的类型的长度小于整型的计算)
C语言中整型算术运算总是以缺省(默认)整型类型(即 int 类型)的精度来计算的,为了获得这个精度,表达式中的字符型(char )和短整型( short )操作数在使用之前要被转换为 int 类型,这种转换称为整型提升。
如何进行整型提升呢?
1.有符号整数提升是按照变量的数据类型的符号位来提升的
2.无符号整数提升,高位补0
例如:
因此,即使时两个char类型相加,也需要先转换成int类型才能相加
整型提升的意义:
(二).算术转换(针对所计算的类型的长度大于整型的计算)
如果某个操作符的各个操作数属于不同类型,那么除非其中一个操作数转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算数转换。
如果某个操作数的类型在上面这个列表中排名靠后,那么首先要转换为另外一个操作数的类型后执行运算。即:如果 int 类型和 double 类型进行计算,则需将 int 类型转换为 double 类型。