10.0.0 多进制
10.1.0 C语言的四种进制
10.1.1 C语言能够识别的进制有哪些?
二进制
逢二进一,每一位数字数用0或者1来表示。
0 1 10 11 100 101 110 111 1000
在C语言中,如果要写一个二进制的数,那么就在二进制数前面加一个0b的前缀。
二进制的每一位是0或者1,逢二进一
C语言中写一个二进制数据需要在前面加上0b。
八进制
逢八进一,每一位的数字只能是1 2 3 4 5 6 7 0
0 1 2 3 4 5 6 7 10 11 12 13 14 15 16 17 20 21 22…
在C语言中,如果要写一个八进制的数,就需要在这个八进制数前面加一个0的前缀。
%o代表将整型变量中的数据以八进制的形式输出出来。
十进制
逢十进一
在C语言中,直接写一个整数,默认就是十进制的
%d代表将整型变量中的数据以十进制的形式输出出来
十六进制
逢十六进一,每一位是0 1 2 3 4 5 6 7 8 9 a b c d e f
0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20
在C语言中,如果要写一个十六进制的数,就需要在这个数前加一个0x前缀
%x代表将整型变量中的数据以十六进制的形式输出出来
10.1.2 四个基本概念
四个基本概念
数码:指的是这个数据的每一位的数字
数位:数码在这个数据中的位置。从右到左依次增加,从0开始。
基数:每一个数码可以有多少个数据来表示。基数其实指的就是这个数的进制。
位权:数码乘以(基数的数位次方)。(任何数的0次方都为1)
10.1.3 十进制与二进制之间的转换
除2取余法:将这个数除以2,直到商为1,然后将余数倒序排列,这就是这个十进制对应的二进制。
比如说:十进制的数10
10除以2,商5余0,5除以2,商2余1,2除以2,商1余0,所以10对应的二进制数是1010.
加权法:将这个二进制数的每个数码的位权相加,就是这个二进制数对应的十进制数。
比如说:二进制数1010
从右往左,每一位的位权分别为:0*2^0+1*2^1+0*2^2+1*2^3=0+2+0+8=10
10.1.4 十进制与八进制之间的转换
除8取余:将这个数除以8,直到商为1,然后将余数倒序排列,就是这个十进制数对应的八进制数。
比如说:十进制数10
10除以8,商1余2,所以10对应的八进制数应该是12。
加权法:将八进制的每一个数码的位权相加,得到的就是其对应的十进制数。
比如说:八进制数12
从右往左,每一位的位权分别为:2*8^0+1*8^1=2+8=10
10.1.5 二进制与八进制之间的转换
三合一法则:将这个二进制从低位到高位每三位分成1组,高位不够补0,将每一组转换为十进制,将每一组的十进制连起来,就是这个二进制数对应的八进制。
比如说二进制数1011100110011
从左往右,每三位分成一组,高位不够补0,于是就变成:
001 011 100 110 011 每一组转化为十进制数,于是变成:
1 3 4 6 3 再将它们连起来,于是变成:13463
于是1011100110011这个二进制数对应的八进制数是13463。
一拆三:将这个八进制数的每一个数码拆为一个三位的二进制数,连起来就是这个八进制数相对应的二进制数。
比如说:八进制数13463
从左往右,将每个数码分开,于是就变成:
1 3 4 6 3 每个数码变成一个三位数的二进制数,于是变成:
001 011 100 110 011 将它们连起来,于是就得到1011100110011
所以八进制数13463的相对应的二进制数是1011100110011。
10.1.6 十六进制与二进制之间的转换
四合一法则:将这个二进制从低位到高位每四位分成一组,高位不够补0,将每一组转换为对应的十六进制,再将每一组转换成功的十六进制数连起来,就是这个二进制数对应的十六进制数。
比如说:二进制数1011100110011
从左往右,将每四个数码分成一组,高位不够补0,于是就变成:
0001 0111 0011 0011 将每一组转换成对应的十六进制数,于是就变成:
1 7 3 3 将它们连起来,于是就变成:1733
所以1011100110011的对应十六进制数为1733
一拆四法则:将这个十六进制的数的每一个数码,拆为1个四位数的二进制数,然后再将其连起来,就得到这个十六进制数对应的二进制数。
比如说:十六进制数1733
从左往右,将每个数拆成四位数的二进制数,于是变成:
1 7 3 3
0001 0111 0011 0011 再连起来就是1011100110011。
八进制转换为十六进制
需要将八进制先转换为二进制,然后再将二进制转化为十六进制。
10.2.0 变量在内存中的存储形式
10.2.1 变量存储的简单细节
内存中的数据存储单元是由一个一个的二进制位组成的。每一个二进制位只能存储0或者1.
如果我们使用1个二进制位来存储数据的话,每一个二进制位只能表示2个数据。于是科学家为了能够方便存储更多数据,它们就把内存中的8个二进制位分为一组,叫做一个字节,作为存储数据的最小基本单位。
如果要往内存中存储数据的话,就至少要使用1个字节,也就是8个二进制位。这个时候,1个字节就可以最多表示256种数据。
单位换算:
8bit = 1byte
1024 bytes = 1 KB
1024 KB = 1MB
1024MB = 1GB
1024 GB = 1 TB
我们在声明变量的时候,就是向系统在内存中申请一个指定字节数的连续的字节空间。
在声明变量的时候,int占4个字节、double占8个字节、float占4个字节、char占1个字节。
10.2.2 sizeof运算符
计算变量、常量在当前系统内存中占用的字节数。
使用方法:
sizeof(数据类型) 就会返回这个数据类型的变量在内存中占据多少个字节。
sizeof(变量名) 就会返回这个变量在内存中占据多少个字节。
sizeof(常量) 就会返回这个常量数据在内存中占据多少个字节。
常量:直接写一个数据,这个数据就是常量。
注意:
double类型的数据变量比float类型的变量多一倍,所以同样12.12这个常量,不加f是double类型的常量,占8个字节,但是加了f之后就只占4个字节。
char类型的变量在内存中占据1个字节,但是char类型的常量在内存中占据4个字节。
简写方法:
在使用sizeof的时候,某些情况下可以直接省略小括号。当判断的东西不是数据类型的时候可以省略小括号。
变量在内存中占据的字节数,会因为系统版本、编译器的不同而发生变化。
10.2.3 原码、反码、补码
无论任何数据,在内存中存储的时候都是以二进制的形式存储的。
原码、反码、补码都是二进制,只不过是二进制的不同的表现形式,数据是以补码的二进制形式存储的。
一个int类型的变量,在内存中占据4个字节,32位
00000000 00000000 00000000 00000000
在不考虑正负的情况下,1个int类型的变量可以表示接近43亿种数据。
为了可以表示正负性,使用最高位来表示这个数的正负性。
如果最高位是0,那么表示这个数是1个正数
如果最高位是1,那么表示这个数是1个负数
所以,用来表示数据的只剩下31位,所以一个int类型的变量,最小值是 -231,最大值是 +231-1。之所以要减1,是因为中间还有一个数是0.
原码:
最高位表示符号位,剩下的位数是这个数的绝对值的二进制。
例如:
10的原码:
00000000 00000000 00000000 00001010
-8的原码:
10000000 00000000 00000000 00001000
-20的原码:
10000000 00000000 00000000 00010100
反码:
正数的反码就是其原码;
负数的反码就是在其原码的基础上,符号位不变,其他位取反。
例如:
10的反码:
10的原码:00000000 00000000 00000000 00001010
10的反码:00000000 00000000 00000000 00001010
-8的反码:
-8的原码:10000000 00000000 00000000 00001000
-8的反码:11111111 11111111 11111111 11110111
补码:
正数的补码就是其原码;
负数的补码就是其反码的基础上+1。
例子:
10
10的原码:00000000 00000000 00000000 00001010
10的反码:00000000 00000000 00000000 00001010
10的补码:00000000 00000000 00000000 00001010
-8
-8的原码:10000000 00000000 00000000 00001000
-8的反码:11111111 11111111 11111111 11110111
-8的补码:11111111 11111111 11111111 11111000
任何数据都是以其二进制的补码形式存储在内存中的。
为什么数据要以补码的形式存储呢?
因为计算机中只有加法没有减法,为了更加低成本的计算出结果,所以使用补码来存储数据。
10.2.4 位运算
位运算:指的是1个二进制数据的每一位来参与运算。
位运算的前提是这个数必须是1个二进制数。
注意:
参与位运算的二进制数据必须是补码形式
位运算的结果也是二进制的补码形式
按位与:&
参与按位与的两个二进制数,每一位上如果都为1,那么结果就是1,只要有一位是0,那么结果就是0.
举例:3 & 2;
第一步骤:先得到两个数的二进制补码形式:
3的补码:00000000 00000000 00000000 00000011
2的补码:00000000 00000000 00000000 00000010
————————————————————————————————————
按位与: 00000000 00000000 00000000 00000010
注意:
任何数按位与1的结果是:是这个数的最低位
偶数的最低位一定是0,奇数的最低位一定是1
所以如果要判断一个数是奇数还是偶数,只要用这个数按位与1就可以了,如果结果为1就是奇数,如果结果为0那么结果就是偶数。
按位或:|
参与按位或的二进制数据,每一位上只要有一个1,那么结果就是1,只有当两位都是0的时候结果才是0.
举例:3 | 2;
3的补码:00000000 00000000 00000000 00000011
2的补码:00000000 00000000 00000000 00000010
————————————————————————————————————
按位或: 00000000 00000000 00000000 00000011
按位取反:~
按位取反是单目运算符,表示将这个二进制数的每一位取反。
举例:~3;
3的补码: 00000000 00000000 00000000 00000011
按位取反:11111111 11111111 11111111 11111100
结果为 -4
按位异或:^
参与按位异或的二进制数据的每一位,如果相同则为0,不同则为1.
举例:3 ^ 2;
3的补码: 00000000 00000000 00000000 00000011
2的补码: 00000000 00000000 00000000 00000010
————————————————————————————————————
按位异或: 00000000 00000000 00000000 00000001
按位左移运算:<<
参与按位左移运算的二进制数据,向左移动指定的位数,低位不够补0,高位溢出就丢弃。
举例:3 << 2; 表示3按位左移2位
3的补码:00000000 00000000 00000000 00000011
左移2位:00000000 00000000 00000000 00001100
注意:
左移运算有可能会改变其正负性;
将1个数左移n位,相当于将这个数乘以2的n次方;
3 << 2 = 12
16 << 3 = 16 * 23 = 128
按位右移:>>
参与按位右移运算的二进制数据,向右移动指定的位数,高位不够补符号位,低位溢出就丢弃。
举例:3 >> 2; 表示3按位右移2位
3的补码:00000000 00000000 00000000 00000011
右移2位:00000000 00000000 00000000 00000000
16 >> 2;
16的补码:00000000 00000000 00000000 00010000
右移2位: 00000000 00000000 00000000 00000100
注意:
右移运算不会改变正负性
1个数按位右移n位,相当于这个数除以2的n次方。
100 >> 2 = 100 / 22 = 25
10.2.5 交换两个变量的值的第三种方法
交换两个变量的值可以用按位异或运算:
int a = 3;
int b = 2;
a = a ^ b; (3 ^ 2 = 1)
b = a ^ b; (1 ^ 2 = 3)
a = a ^ b; (1 ^ 3 = 2)
10.2.6 深入变量的细节
变量占用的字节空间一定是连续的。
内存中存储数据的最小基本单位是字节。
每一个字节都有一个独一无二的内存地址,是一个十六进制的数。
并且相邻的字节的地址一定是连续的。
声明一个变量就是在内存中分配连续的指定字节数的空间。
在位变量分配字节空间的时候,是从高地址向低地址分配的,分配连续的指定字节数的空间。
存储在变量中的数据是以数据的二进制的补码形式存储进去的。
存储的时候,是低位存储在低字节,高位存储在高字节。
变量的地址:变量的地址是组成这个变量的低字节的地址。
使用&取地址可以取出变量的地址。
&变量名; 这个表达式的结果就是这个变量的地址
要打印地址使用%p占位符:
printf(“num1的地址是:%p\n”,&num1);
10.2.7 int类型的修饰符
一个int类型的变量在内存中占据4个字节,所以1个整型的变量可以保存正负21亿之间的整数。
存在一个问题,有一些大于int范围的整数就无法存储,而很小的数据又浪费了很多空间。
于是引入了int类型的修饰符。
作用:指定int类型的变量在内存中占用的字节数。
short修饰符:
在声明一个int类型的变量的时候,可以使用short来修饰。
short int num = 10;
被short修饰的int类型的变量在内存中只占据2个字节,16位。
因为最高位是表示正负的,所以数据位是15位。
所以最小值是-32768,最大32767.
要输出short修饰符的int类型变量的值应该使用%hd为占位符。
在声明时,int可以省略,直接写short num = 10;
long修饰符:
在声明一个int类型的变量的时候,可以使用long来修饰。
long int num = 10;
在32位操作系统下,被long修饰的整型变量占据4个字节;
在64位操作系统下,被long修饰的整型变量占据8个字节。
所以,在64位操作系统下,long修饰的整型变量最小值是-263,最大值是263-1。
输出该类型的变量应该使用%ld占位符;
在声明时,int也可以省略,直接写long num = 10;
long long修饰符
在声明一个int类型的变量时,可以用long long来修饰。
long long int num = 10;
无论在32位还是64位操作系统下都是占据8个字节。
输出该类型的变量应该使用%lld占位符;
在声明时,int也可以省略,直接写long long num = 10;
unsigned修饰符
声明int变量的时候,为这个变量加一个unsigned修饰符,表示这个变量的最高位不要用来表示正负,而是参与到数据的表示之中。
unsigned int num;
那么这个变量的最小值变成了0,但是最大值变成了232。
使用%u输出unsigned int变量的值。
unsigned还可以用在其他修饰符的前面:
unsigned short %hu
unsigned long %lu
unsigned long long %llu
signed
表示最高位用来表示正负符号。实际上默认就是如此,所以写不写都可以。
10.2.8 char变量的深入
字符数据在内存中存储的是这个字符所对应的ASCII码的二进制补码。
char变量也是有符号位的。
我们可以直接为char变量赋值一个整数,也可以使用%d输出char变量的值,因为char变量本来存储的就是整数。