4 信息的表示和处理_信息存储
开头:本章研究在计算机上如何表示数字和其它形式数据的基本属性,以及计算机对这些数据执行操作的属性。
1 内存空间和内存地址
注意:这部分谈到的内存,并不是指硬件中的内存条,而是在《计算机系统漫游》章节中的:【虚拟内存】:是对主存、磁盘、I/O设备的抽象表示
下面是书本的描述:
- 字节(byte):大多数计算机使用8位的块,或叫做字节(byte),来作为最小的可寻址的内存单位,而不是访问存储器中单独的位
- 虚拟内存:把内存看作成一个非常大的字节数组。
- 地址:内存中的每个字节都由一个唯一的数字来标识。一般是用十六进制来表示。
- 虚拟地址空间:所有可能地址的集合。
这里是我的总结:
字节:存储空间的最小单位,描述数据占据多少空间,就用字节来数表示。大多数机器中,1个字节=8位
地址:内存中的每个字节都由一个唯一的数字来标识,成为它的地址;理解为门牌号。
疑问:地址占不占用空间?
- 地址本身不占用内存空间的
- 有时需要将数据的地址存储起来,用于对该数据的访问,这时地址它是占用空间
- 如果把地址放在指令中来执行,此时地址它是不占用空间。
2 字长
关于字长:平时说的32位系统、64位系统,34 跟 64 表示字长。
本节先这么理解字长:
- 字长:决定了虚拟地址空间的最大值。它是操作系统层面你的参数
- 对于一个字长为x位的机器而言,虚拟地址范围为0~2x-1,程序最多访问2x字节。
- 字长32位:限制寻址空间为4GB
- 字长64位:限制寻址空间为16EB
## 推导过程
1024 = 2^10
1EB=1024PB
1PB=1024TB
1TB=1024GB
1GB = 1024MB
1MB=1024KB
1KB = 1024B
1GB = (1024 * 1024 * 1024) B = 2^30B
所以:
2^32 = 2^30 * 2^2 = 4GB
2^64 = 2^30 * 2^30 * 2^4 = 16EB
16EB = 16 * 1024 *1024*1024 = 17179869184GB
C语言数据类型中在不同字长系统下的占据字节数如下:
3 十六进制
二进制示法太冗长,而使用十进制表示法,与位模式的互相转化很麻烦。替代的方法是:十六进制数:使用数字“0”“9”,以及字符“A”“F”来表示16个可能的值。
- 0x 或 0X 开头表示
- 字符“A”~“F”既可以是大写,也可以是小写,至是 大小写混合
2个十六进制数字就表示一个字节:因为用4个二进制数字就可以表示1个16进制数字,因此2个十六进制=2*4=8个二进制 =1个字节
二进制转十六进制:**从右往左,每四位为一截,划分,左边不足四位,补0**
十六进制转二进制进制:分别将每个16进制分别转换成对应的二进制即可。例如 0x8A = 1000 1010
十进制转二进制:除2 取余,再反转排序
二进制转十进制:abcd.efg(2)=d20+c21+b22+a23+e2-1+f2-2+g* 2-3
4 字节顺序
对于跨越多字节的程序对象,需建立两个原则:这个对象的地址是什么?以及在内存中如何排列这些字节。
- 对象被存储为连续的字节序列,对象的地址为存储在最低地址的那个字节的的地址
- 排序规则有大端法和小端法
例如,假设一个类型为int的变量x的地址为 0x100,x的4字节将被存储在内存的 0x100、0x101、0x102 和 0x103 位置。
- 大端法:高位存储在低地址
- 小端法:低位存储在低地址
不同的系统有各自的表示规则,
两种不同排序规则下的字节序列存储如下:
关于设定好字节顺序的两种场景
- 网络字节顺序
- 反编译的场景
网络上的数据流是字节流,对于一个多字节数值,在进行网络传输的时候,先传递哪个字节?也就是说,当接收端收到第一个字节的时候,它是将这个字节作为高位还是低位来处理呢?发送方要根据接收方的字节排序规则,来确定字节的发送顺序
网络字节序定义:收到的第一个字节被当作高位看待,这就要求发送端发送的第一个字节应当是高位。而在发送端发送数据时,发送的第一个字节是该数字在内存中起始地址对应的字节。可见多字节数值在发送前,在内存中数值应该以大端法存放。
判断机器大小端法例子
#include <stdio.h>
int main()
{
int a = 0x12345678;
char i = a;
printf("%x", i);
return 0;
}
定义1个十六进制的数据,数据类型为int型,再定义一个char类型的数据,int数据类型的大小为4个字节,而char类型的数据为1个字节,
因此将int类型的数据赋值给char时会丢失三个字节的数据,char类型中存储的是int类型中低地址的数据,
这时候char类型获取的数据输出以后,若是输出的是12那就说明你低地址位置的数据是12,那就说明你的数据是大端存储,若是输出的结果是78那当前条件下就是小端存储。
输出结果:78,即该centos系统采用小端法存储
5 机器代码跨平台不兼容
计算机系统的一个基本概念就是,从机器的角度来看,程序仅仅只是字节序列。机器没有关于原始源程序的任何信息,除了可能有些用来帮了助调试的辅助表以外。
不同的机器类型使用不同的且不兼容的指令和编码方式。即使是完全一样的进程,运行在不同的操作系统上 也会有不同的编码规则,因此二进制代码是不兼容的。二进制代码很少能在不同机器和操作系统组合之间移植。
6 C语言的三种运算
6.1 位级运算
符号 | 表述 | 说明 |
---|---|---|
! | OR | |
& | AND | |
~ | NOT | 取反 |
^ | EXCLUSIVE-OR 异或 | 位数相同得到0,位数相反得到1 |
举例子:
将十六进制转换成二进制并执行二进制运算后,再转回十六进制。
6.1.1 引入掩码
什么是掩码?:
简单来说掩码就是一串二进制码。比如:00000101。就是个数字。掩码的作用是用来存储和操作“状态(State)”。大致用法和逻辑总结:
- 用二进制码存储状态,
- 通过位运算(&, |, ^, ~, >>, << 等)来操作状态。
为什么使用掩码?
为什么使用掩码其实本质是为什么使用二进制和位运算。
在计算机编程中,直接做二进制运算——即位运算的效率更高。而且表达上更简洁。
哪里有使用?
熟悉Java的都知道,java.util.concurrent JUC工具包中,大量的类使用位级运算,保存状态或结果信息,例如AbstractQueuedSynchronizer的成员变量:state,可重入读写锁ReentrantReadWriteLock就是利用了左移右移运算,同时在高、低位存储了读/锁的状态
6.2 逻辑运算
逻辑运算符号 | 表述 | 说明 |
---|---|---|
|| | OR | 逻辑或 |
&& | AND | 逻辑与 |
! | NOT | 取反 |
注意:逻辑运算跟位级运算容易混淆,但是他们的功能完全不同。
- 逻辑运算认为所有非零的参数都表示TRUE,而参数0表示FALSE。因此逻辑运算结果只有两种:0/1,代表着true或者faslse。
- 如果前面的参数求值就能确定表达式的结果,那么逻辑运算就不会对第后面的参数求值。例如(1==1)||(1/0),将不会造成被零除
6.3 移位运算
6.3.1 左移 <<
左移K位 : 按二进制形式把所有的数字向左移动K位,高位舍弃,低位的空位补零。
数字意义:在数字没有溢出的前提下,对于正数和负数,左移一位都相当于乘以2的1次方,左移n位就相当于乘以2的n次方
4<<2:表示10进制4左移2位。结果:0000 0100<<2 得到:0001 0000 = 16
6.3.2 逻辑右移>>
也称为无符号右移。
右移K位 : 低位舍去K位,高位补上K个0
对参数x:0110 0011 ,逻辑右移4位,结果:0000 0110
6.3.3 算术右移>>
右移K位 : 低位舍去K位,高位补上K个x,这个x(符号位)是移位后最高位的那个值
对参数x:1001 0101,算术右移4位,
右移四位后:1001 ,此时最高位是1
补充K个最高位值后:1111 1001
6.3.4 关于右移的补充说明
- C语言标准并没有明确定义对于有符号数应该使用哪种类型的右移--算术右移或者逻辑右移都可以。
- 实际上,几乎所有的编译器/机器组合都对有符号数使用算术右移
- 对于无符号数,右移必须是逻辑的。
- 与C相比,Java对于如何进行右移有明确的定义:表达是 x>>k会将x算术右移 k个位置,而 x>>>k会对x做逻辑右移。
6.3.5 为什么会有移位运算
- 整数乘法、除法运算指令慢,需要耗费更多的时钟周期。慢
- 位运算需要耗费的始时钟周期少。快
- 用位运算和加法运算的组合来代替常数的乘法运算。左移
- 用位运算来代替除法。 右移![image]