4 信息的表示和处理_信息存储


开头:本章研究在计算机上如何表示数字和其它形式数据的基本属性,以及计算机对这些数据执行操作的属性。

1 内存空间和内存地址

注意:这部分谈到的内存,并不是指硬件中的内存条,而是在《计算机系统漫游》章节中的:【虚拟内存】:是对主存、磁盘、I/O设备的抽象表示

下面是书本的描述:

  • 字节(byte):大多数计算机使用8位的块,或叫做字节(byte),来作为最小的可寻址的内存单位,而不是访问存储器中单独的位
  • 虚拟内存:把内存看作成一个非常大的字节数组。
  • 地址:内存中的每个字节都由一个唯一的数字来标识。一般是用十六进制来表示。
  • 虚拟地址空间:所有可能地址的集合。

这里是我的总结:

字节:存储空间的最小单位,描述数据占据多少空间,就用字节来数表示。大多数机器中,1个字节=8位
地址:内存中的每个字节都由一个唯一的数字来标识,成为它的地址;理解为门牌号。

疑问:地址占不占用空间?

  1. 地址本身不占用内存空间的
  2. 有时需要将数据的地址存储起来,用于对该数据的访问,这时地址它是占用空间
  3. 如果把地址放在指令中来执行,此时地址它是不占用空间。

2 字长

关于字长:平时说的32位系统、64位系统,34 跟 64 表示字长。
本节先这么理解字长:

  1. 字长:决定了虚拟地址空间的最大值。它是操作系统层面你的参数
  2. 对于一个字长为x位的机器而言,虚拟地址范围为0~2x-1,程序最多访问2x字节。
  3. 字长32位:限制寻址空间为4GB
  4. 字长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 字节顺序

对于跨越多字节的程序对象,需建立两个原则:这个对象的地址是什么?以及在内存中如何排列这些字节。

  1. 对象被存储为连续的字节序列,对象的地址为存储在最低地址的那个字节的的地址
  2. 排序规则有大端法和小端法
例如,假设一个类型为int的变量x的地址为 0x100,x的4字节将被存储在内存的 0x100、0x101、0x102 和 0x103 位置。
  • 大端法:高位存储在低地址
  • 小端法:低位存储在低地址

不同的系统有各自的表示规则,

两种不同排序规则下的字节序列存储如下:

关于设定好字节顺序的两种场景

  1. 网络字节顺序
  2. 反编译的场景

网络上的数据流是字节流,对于一个多字节数值,在进行网络传输的时候,先传递哪个字节?也就是说,当接收端收到第一个字节的时候,它是将这个字节作为高位还是低位来处理呢?发送方要根据接收方的字节排序规则,来确定字节的发送顺序

网络字节序定义:收到的第一个字节被当作高位看待,这就要求发送端发送的第一个字节应当是高位。而在发送端发送数据时,发送的第一个字节是该数字在内存中起始地址对应的字节。可见多字节数值在发送前,在内存中数值应该以大端法存放。

判断机器大小端法例子

#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 取反

注意:逻辑运算跟位级运算容易混淆,但是他们的功能完全不同。

  1. 逻辑运算认为所有非零的参数都表示TRUE,而参数0表示FALSE。因此逻辑运算结果只有两种:0/1,代表着true或者faslse。
  2. 如果前面的参数求值就能确定表达式的结果,那么逻辑运算就不会对第后面的参数求值。例如(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 关于右移的补充说明

  1. C语言标准并没有明确定义对于有符号数应该使用哪种类型的右移--算术右移或者逻辑右移都可以。
  2. 实际上,几乎所有的编译器/机器组合都对有符号数使用算术右移
  3. 对于无符号数,右移必须是逻辑的
  4. 与C相比,Java对于如何进行右移有明确的定义:表达是 x>>k会将x算术右移 k个位置,而 x>>>k会对x做逻辑右移。

6.3.5 为什么会有移位运算

  1. 整数乘法、除法运算指令慢,需要耗费更多的时钟周期。
  2. 位运算需要耗费的始时钟周期少。
  3. 用位运算和加法运算的组合来代替常数的乘法运算。左移
  4. 用位运算来代替除法。 右移![image]
posted @ 2022-09-13 20:12  拿了桔子跑-范德依彪  阅读(198)  评论(0编辑  收藏  举报