【深入理解计算机系统 02】信息的表示和处理

信息的表示和处理

01 信息的存储

虚拟地址空间

数据的最小单位是bit,一个比特表示一个1或0,8个比特组成一个Byte

  • 二进制单字节范围:00000000~11111111
  • 十进制单字节范围:0~255

一般来说,程序会将内存视为一个非常大的数组,数组又由一个个的字节组成(图中的一个方框就是一个字节),每个字节通过唯一的一串数字来表示,一般称为地址,所有地址的几何就是虚拟地址空间

在这里插入图片描述

虚拟空间的最大空间范围是由机器字长决定的,对于字长为w位的机器,范围为$2^w-1 $

  • 32位计算机地址空间 0 ∼ 2 32 − 1 0\sim2^{32}-1 02321,4GB
  • 64位计算机地址空间 0 ∼ 2 64 − 1 0\sim2^{64}-1 02641,16EB
  • 32位机器在嵌入式场景中仍有应用,64位机器是日常设备主流,且基本能向后兼容
  • C语言的long、指针类型在两种机器上所占字节数大小有差别,其他基本一致
  • 在64机器上可以自定义命令来生成可以在32位机器上运行的程序
linux> gcc -m32 -o hello32 hello.c
linux> gcc -m64 -0 hello64 hello.c

数据在虚拟地址空间中的存储有大端法和小端法两种

  • 大端法:数据低位存储在地址高位(大多数IBM、Sun)
  • 小端法:数据低位存储在地址低位(大多数Intel兼容机、Android、iOS)
  • 也有支持双端法的处理器,例如基于ARM架构的处理器

在这里插入图片描述
在这里插入图片描述

C语言的变量类型

  • int和float同样表示一个数,字节模式(该数存储成16进制的值)是完全不同的
  • 字符串为以NULL结尾的字符数组,故abcde字符串的长度为6

02 整数表示与编码

有符号的二进制数的表示

十进制数有正负之分,二进制数也有,带正负的二进制数称为真值

在计算机中通常在有符号数的前面增加1位符号位,用0表示正号,用1表示符号

  • 这种用0和1表示正负号的数称为机器数
  • 目前常用的机器数编码方式有源码、反码和补码

原码、反码、补码

原码

正数符号位用0,负数符号位用1,其余数位表示数值本身

例如,+1010110的原码为01010110-0110101的原码为10110101

原码表示很简单,但是用原码表示的数进行加减法的运算很麻烦,例如异号相减

反码

正数的反码与原码相同,负数的反码是在原码的基础上保持符号位不变,其余各位按位求反

例如,+1010110的反码为01010110-0110101的反码为11001010

补码

正数的补码与原码相同,负数的补码是在原码的基础上保持符号位不变,其余各位按位求反加一,即负数的补码是其反码加1

例如,+1010110的补码为01010110-0110101的补码为11001011

计算机中有符号整数常用补码形式存储,且任意一个数补码的补码是原码

为什么补码的计算规则是这样的,有什么意义?

  • -5,补码为1011,从二进制补码能快速得出对应十进制为-8+2+1=-5
    • 对于4bit数据,补码能表示的最小数字为1000,即-8
    • 对于4bit数据,补码能表示的最大数字为0111,即7
    • 对于8bit数据,补码能表示的最小数字为1000 0000,即-128
    • 对于8bit数据,补码能表示的最大数字为0111 1111,即127
  • 当两个数做减法时,可以用加上一个数的补码代替减去那个数

在这里插入图片描述

有符号数和无符号数转换

C语言允许数据之间做强制类型转换,例如

short int a =-12345;
unsigned short b = (unsigned short)a;
printf(”a= %d , b = %u” , a, b);

-12345经过强制类型转换的得到无符号整数53191

  • 1100 1111 1100 0111对应-12345
  • 1100 1111 1100 0111对应53191

有符号转无符号

  • 如果是正数,就相同
  • 如果是负数,就是原来的值加上$2^w $,其中w为类型长度(包含符号位)

无符号转有符号

  • 如果是正数,就相同
  • 如果是负数,就是原来的值减去$2^w $,其中w为类型长度(包含符号位)

为什么要了解这个转换:C语言隐式转换影响运算结果

int i = -1;
unsigned int b = 0;
if(a < b)
  printf(”−1 < 0”)
else
  printf(”−1 > 0”)

由于b是无符号整数,实际和0比较的不是-1,而是转换后的4294967295( − 1 + 2 32 -1+2^{32} 1+232

扩展一个数字的位表示

从unsigned char(8)转变为unsigned short(16)类型,只需要在扩展数位进行补0即可

在这里插入图片描述

当有符号数表示非负数时,最高位是 0,此时扩展的数位进行补零即可;当有符号数表示负数时,最高位是 1,此时扩展的数位需要进行补 1

在这里插入图片描述

当有符号数从一个较小数据类型转换成较大类型时,进行符号位扩展,可以保持数值不变

截断一个数字的位表示

将int类型强制转换成short类型时,int类的最高位16位数据被丢弃,留下低16位数据

  • 无符号数
    • w位的无符号整数,截断成k位。可用于取模运算,即除以2的k次方之后得到的余数
  • 有符号数
    • 用无符号数的函数映射,使用与无符号数相同的截断方式,得到最低K位
    • 将上一步得到的无符号数转成有符号数

03 整数运算

无符号数加法溢出

加法本身很简单,但是加法可能会导致溢出

unsigned char a = 255;
unsigned char b = 1;
unsigned char c = a + b;
printf(” c=%d”, c);

两者相加的结果c会溢出,实际结果不是256而是0

对于溢出是不会报错的,需要手动添加运算结果判定

有符号数加法溢出

计算机的有符号数用补码表示,因此补码加法就是有符号加法

char x = 127;
char y = 1;
char z = x + y;
printf(” z=%d”, z);

与无符号数不同,有符号数的溢出有正溢出和负溢出

  • 正溢出会在原有结果上减去2的w-1次方,w为类型长度
  • 负溢出会在原有结果上加上2的w-1次方,w为类型长度
  • 该代码的运行结果为-128,发生了正溢出

无符号数乘法

w位的无符号数x和y,乘积的结果依然是w位,相当于x与y的乘积结果对2的w次方取模(除以2的w次方的余数)

有符号数乘法

结果同无符号数乘法一致,也是w位,但多了一步将无符号数转化为有符号数

位移快速运算(乘除2的倍数)

原码运算

对原码进行移动,移动单位等于2的倍数,乘左移补0,除右移补0

补码运算

正数同原码一致

负数,保持符号位不变,其余位数乘法左移补0,除法右移补1

04 浮点数

IEEE浮点表示

V = ( − 1 ) s × M × 2 E V=(-1)^{s} \times M \times 2^{E} V=(1)s×M×2E

三个变量:符号s、阶码E、尾数M

在这里插入图片描述
在这里插入图片描述

浮点数的数值通过阶码划分为3类

  1. 规格化:阶码不全为0且不全为1(exp)
  2. 非规格化:阶码全为0
  3. 特殊值:阶码全为1,特殊值也分两类,一类表示无穷大或无穷小,一类表示不是【

{待完善:三种类型的具体表示方法}

05 程序编码

演示程序

#include <stdio.h>
void mulstore(long ,long ,long *);
int main() {
  long d;multstore(2,3,&d) ;
  printf(” 2∗ 3 −−>%1d \n”,d) ;return 0;
}
long mult2(long a,long b){
  png s = a* b;return s;
}

两个源文件可以通过Linux> gcc -Og -o prog main.c mstore.c生成可执行文件

编译选项-Og是用来告诉编译器生成符合原始C代码整体结构的机器代码

  • 为了获得更高的性能,会使用-O1或者-O2,甚至更高的编译优化选项
  • 但高级别的优化产生的代码会严重变形,和源代码的关系难以理解

生成汇编文件

可以通过linux> gcc -Og -S mstore.c生成汇编文件mstore.s

两种保存器

调用者保存寄存器,被调用者保存寄存器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值