内存的最小单位是字节,一个字节等于8位(bit),每一位要么是0要么是1,也就是用二进制来表示。spa
一个字节在内存中的表示为:3d
无符号二进制转成十进制公式:code
w
:二进制位的长度。i
:二进制位从右往左开始的下标,从0开始计数。w-1
:因为i是从0开始计数,因此最后一个下标就是w-1。x(i)
:第i
位的值,要么是0要么是1。2^i
:2的第i
次幂。例如:
无符号二进制数10010
按照公式展开就是:blog
若是把这个数用1个字节在计算机中存储,内存中就表示为:内存
不足8位,左边补0。rem
1个字节的无符号正式能表示2^8 = 256
个不一样的数。能表示最大的数是8个二进位全是1的数等于255,也就是求一个公比为2
,首项是1
的等比数列
前8项和。二进制位求和公式为(2^n) - 1
。总结下来一个n位的二进制数能表示最大的数是(2^n) - 1
,可以表示2^n
个不一样的数,之因此是2^n个不一样的数,是由于能够表示0~(2^n) - 1
,从0开始的因此还须要+1
个长度。it
Char类型是用来存储单个字符,在内存中占用1个字节的大小,它使用8个bit来表示256个字符。
Char类型实际存储的是字符的ASCII
码,因为ASCII
码是整数。因此Char最终在内存中是一个8bit的整型。
好比字符A
的ASCII
码是65,65 = 2^0 + 2^6,因此在内存中的表示为:class
char ch = 'A'; printf("%d", ch); // output is 65
Short 表示的是短整型,通常占用2个字节的内存大小。
它的取值范围是(-2)^15~(2^15)-1
包含0。最大值这里是(2^15)-1
,是由于short有符号位,须要用最高位(用从左到右第一位)来表示符号,0表示正数,1表示负数。 最大值的二进制表示为0111111111111111
(16个二进制位),十进制就是(2^15)-1
。 之因此是(2^15)-1
,也是以前说的求和公式((2^n)-1
。基础
二进制加减法和十进制同样,把对应位
相加,大于1就向前进位。例如0111 + 1 = 1000
若是想要把7和-7相加使结果等于0。按照在计算机中使用二进制的最高位来当作符号位的,0表示正数,1表示负数。那么7表示为0000111
,-7就表示为1000111
。0000111 + 1000111 按照二进制先前的加法法则得出来是1001110
,结果不是咱们想要的0。变量
怎么才能让2个二进制数相加获得0呢?
想要获得0,就须要利用进位,好比在11111111
(8个1)的基础上加1就能够获得100000000
(一共9位,左边第一位是1,后面8个0) ,舍掉最左边的那个1就获得了8个0最终结果就等于0。把原码按位取反而后与原码相加就能够获得全1的二进制数。好比0000111
按位取反就是1111000
,他们俩相加获得11111111
。 再把它加1就获得最后的结果0。整个过程须要3步,咱们把最后两步合并成一个步骤,也就是把按位取反和加1合并到一块儿,其实就是把原码的反码加1。如1111000
加1获得1111001
。最后这两步合在一块儿叫作取原码的补码。最后获得的1111001
就叫作0000111
的补码。
好比正整数7
的二进制码是0000111
,它的补码仍是它自己。再好比-7
对应的正整数二进制码是0000111
,它的反码就是1111000
(把原码按位取反)。而后再加1
就获得1111001
。1111001
就是-7
的补码。咱们再次把1111001
和0000111
按照二进制加法法则相加恰好获得0。这里须要注意的是,这里左边会产生一个溢出位,这个溢出位是去掉不要的,获得结果就是0。
-1的补码全是1,由于它加上1以后就变成了0。
计算机系统都是用补码来表示二进制码,这样的好处之一就是可让加减法运算统一处理。
当把char
类型的变量赋值给short
类型的变量时,会把char
的8个bit放在short
的低八位(从右往左第一个字节)上。
例如:
char ch = 'A'; // 'A' ASCII:65 内存表示为 01000001 short s = ch; // 内存表示为 00000000 | 01000001
一个特殊的状况就是当把一个short
的-1
赋值给一个int
变量的时候,并不会获得00000000 | 00000000 | 11111111 | 11111111
,由于若是这样的话表示的值就不是-1
了。因此正确的作法就是把全部的1
所有拷贝给int
。
例如:
short s = -1; // 内存表示为 11111111 | 11111111 int i = s; // 内存表示为 11111111 | 11111111 | 11111111 | 11111111
相反若是把short
类型的变量赋值给char
类型的变量时,会把short
的低八位(从右往左第一个字节)放在char
仅有的一个字节上。会把多的字节自动剔除。
例如:
short s = 65; // 内存表示为 00000001 | 01000001 char ch = s; // 内存表示为 01000001
咱们已经知道无符号二进制转成十进制公式为:
这里的i
是从0开始的也就是从右边的第一位是2^0
,若是咱们从一个负整数开始的话,就会存在负整数次幂,那么也就会出现小数部分了。
例若有一个16位的二进制数000000011 | 11000000
用它的前八位来表示整数部分,后八位来表示小数部分,就也能够这样表示000000011.11000000
。这样后八位也就再也不是整数次幂了,而是从左到右每一位分别是2^(-1)~2^(-8)
。这个数就能够表示成:
这是其中一种浮点数表示方法,这种方法表示的浮点数会出现精度不够,表示的数值区间比较小,因此计算机实际并无用该方法来表示浮点数。
下面这种方法就是计算机内部真实表示浮点数的方法。
咱们先来看下十进制的科学计数法,用科学计数法表示123.45的话就是1.2345 * 10^2
。其中1.2345 为尾数
,10为基数
,2为指数
。计算机在表示浮点数的时候,也借用了十进制的科学计数法的思想,只不过基数为2
了。
例如1000.01
能够表示成1.00001 * 2^3
,几回幂,小数点就向右移动几位。
用32位
的float
来举例,首位是符号位S
,紧跟后面8位
是指数位E
,最后23位
称为尾数位M
。
计算公式:
S为0时恰好是正数,为1时是负数。
它的取值范围是1≤M<2
,取值方式是从左到右每一位分别表示的是2^-1~2^-23
,值就是而后对各个位的表示值求和,这里跟先前浮点数表示的办法一致,都是从负整数次幂开始。因为尾数的整数部分始终都是1
,因此这个1
能够被省略,这样就能够多出一位来提高精度。
减去127是由于偏移量是127。
例如0 | 10000010 | 11110000000000000000000
的每一部分别是:
0
表示整数。
11110000000000000000000
这里须要再加1,由于为了提高小数精度省略了1,因此要加回来。因此完整的尾数部分应该是
1.1111
(省略了后面的0)。 2^0 + 2^-1 + 2^-2 + 2^-3 + 2^-4 = 1.9375
10000010
2^7 + 2^1 = 130
分别带入公式得:
二进制形式:
十进制形式:
详细过程:1 * 1.1111 * 2^(130-127)
=> 1 * 1.1111 * 2^3
=> 1 * 1111.1
(几回幂,小数点就向右移动几位) => 1 * (2^3 + 2^2 + 2^1 + 2^0 + 2^-1)
=> 1 * (8 + 4 + 2 + 1 + 0.5)
=> 15.5
当咱们在把浮点数与整数相互赋值的时候,并不会直接拷贝bit位,而是从新计算出在新的类型中的位模式。
例如:
int i = 5; // 内存表示 00000000 | 00000000 | 00000000 | 00000101 // 从新计算5在float中的表示方式 float f = i; // 内存表示 0 | 00000000 | 00000000000000000000101 printf("%f", f) // output is 5.0
来一点更刺激的!!!
// 2^30 int i = 1073741824; // 内存表示 01000000 | 00000000 | 00000000 | 00000000 // 这里就不会从新计算在float中的表示方式了,而是直接把bit位拷贝过去。用float的解析方式去解析int的那块内存。 float f = *(float *)&i; // 内存表示 0 | 10000000 |00000000000000000000000 // 1 * 2^(128-127) * 1 = 2 printf("%f", f) // output is 2.0
这里就不会从新计算1073741824
在float中的表示方式了,而是直接把int
的bit位拷贝过去
。用float的解析方式去解析int的那块内存
。