深入理解计算机系统-第二章

本文深入探讨了计算机内部的数据表示方法,包括二进制、十六进制与十进制之间的转换,移位操作,原码、反码、补码的概念及应用。此外,还详细解析了整数和浮点数的运算规则,以及不同数据类型间的转换问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

信息存储:

8 bits constitutes one byte,which is the smallest available storage unit。机器级程序将内存视为一个非常大的字节数组,称为虚拟内存visual storage。内存中每一个字节都由一个数字来唯一标识,就是address。And the union of all the possible address consititutes Virtual Address Space。这个虚拟空间只是一个概念性映像,实际的实现是将动态随机访问寄存器、闪存、disk、etc软件与硬件结合起来为程序提供一个看似统一的字节数组。

 

接下来我们简单看一下16进制,这个不难:

In C-language,以0x(0X)开头的我们都认为是16进制,大小写不区分,比如0xFA1D37B,二进制与16进制相互转化也非常弱智,不多说。

接下来的10进制和binary、hexadecimal的转化,这是数学的问题,用c++的stl模板库可以很快写出来。

 

Each computer has its own word size,which decides the most important parameter--The biggest virtual address space.In other words,for a machine with a w-word-size ,the range of VAS is zero to pow(2,w) -1。32位字长的机器限制虚拟地址空间位4GB,而现在大部分机器都是64位机器了。

————————————————————————————————————————分界线

这是在linux环境写的课程笔记:(课后再继续补充)

逻辑数据的编码表示:

1.表示:1/0序列

2.>>,<<,&等位运算

3.位串:用来表示若干个状态位or控制位

比如x86的标志寄存器就是由很多位串来实现的。比如中断interrupt。

 

移位操作:对象是x,移动y位(箭头指向的方向就是左移和右移的方向)

左移:x<<y,方法:比如x<<3,把最前面的三位剔除,然后后面补零

比如:x:101 000 10

x<<3: 000 10 000

右移:分为逻辑右移和算术右移。

比如x:101 000 10

Log. >>2: 00  101 000 (把后面两位剔除,然后前面补零)

Arith.>>2: 11 101 000(Reduplicate the most significant bit)

原码、反码、补码:

原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值。(就符号位而言,0表示+,1表示-)
反码的表示方法是:正数的反码是其本身;负数的反码是在其原码的基础上, 符号位不变,其余各个位取反。
补码的表示方法是:正数的补码就是其本身;负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1。 (即在反码的基础上+1)。-128~+127.

而大家会考虑-128,其实用[0000 0000]表示0,用[1000 0000]表示-128。 而-127:把127换成二进制为1111111在前取负数符号1为原码:11111111,保留符号位,其余0变1,1变0为反码:10000000,在反码上加1为补码:10000001。

老师的练习题:

『x』补=(96)h,求x=(?)d。

(96)h=(1001 0110),

则x= - 110 1001 +“ 1”=-110 1010(2)= -106.

再提一下,补码11101代表的十进制数可以这么理解:-2^4+2^3+2^2+0+2^1

西文字符的编码表示:有限字符->所有单词

Premise:必须熟悉的ASCII码:十进制数字、英文字母(A为65,用16进制,就是41H『展开成8为2进制算位权可得』))、控制字符(是不可打印或显示的)回车0DH、换行0AH。

二进制补码2-complement简例:

10= 0 1 0 1 0(直接凑就行)

-10= 1 0 1 1 0.(-16 + 4 +2)

INT_MIN &INT_MAX:

在32位系统中,int类型的最大值是0x7fffffff(即除了最高的1Bit其他31位都为1),而最小值是0x80000000(除了最高1bit,其他31位都为0)。NOT INT_MIN =INT_MIN保持不变。

————————————————————————————————————————————————————

四则运算:

对于无符号加法,超出则取模,乘法也同理。

补码加减运算:[A+B]补 =A补 + B补

减法是通过取反再相加实现,借助Adder。最高的符号位MSB一同参与运算。n位二进制补码的范围- 2^n~2^n -1 。所以我们统称为加法

对于补码加法:

每一个x\y都位于-2^(w-1)<= x+y-2^w,加法结果不超过界限,该多少是多少;如果出现正溢出,则结果减去2^w;如果出现负溢出,则结果加上2^w.其原则都是遵循取模的原则,构成一个循环结构。

另外,对于负数取反,大部分负数取其相反数是等于其实际的相反数,但是如果这个负数是Tmin(-2^(w-1)),那么它的相反数等于其自身。【

然后我们再看看乘法:

对于无符号数,相乘再取模2^w即可。

对于补码乘法而言:

2个w位的数字相乘可能是2w量级的,为了得到实际数字。我们采用拆分的方式理解,比如:运算A*B,

则A=(Xw-1,Xw-2,Xw-3,。。。,X0)= -Xw-1*2^(w-1)+(Xw-2,...X0),就是首位符号位加非首位部分,首位的值是2的w-1次方乘以数值再乘以-1,非首位保留为二进制串。然后进行乘法分配率运算得到四项求和即可。举个例子,第一项:A的符号位部分为- 1 *2^(4-1),B的非符号位部分为0011,二者相乘先移位,再取反加一得到结果的补码(也就是,取得它的相反数的补码的操作)。这样就可以简洁实现截断保留w位。

————————————————————————————————————————————————

无符号数和有符号数的运算与比较时的规则:

unsigned int和int做运算会将int看成unsigned int,而且结果也是unsigned int。

比如:

#include <iostream>

using namespace std;

 

int main()

{

         int a = -1;

         unsigned int b = 16;

         if(a > b)

                  cout<<"负数竟然大于正数了!\n";

         return 0;
}

这是因为a和b进行比较的时候,编译器将有符号数a看成了无符号数,然后再和b进行比较,在内存中(32位)

a : 11111111 11111111 11111111 11111111

b : 00000000 00000000 00000000 00010000

案例的链接:https://www.cnblogs.com/qingergege/p/7507533.html

——————————————————————————————————————————————

整形的溢出:整形的溢出分为两类,有符号数和无符号数的溢出。溢出可能会造成缓冲区溢出,从而出现漏洞造成黑客的攻击。

首先我们来看看无符号数的溢出:

unsigned char c=0xff;
printf("%d",++c);

//程序输出0

也就是说,对于unsigned整型溢出,C的规范是有定义的——“溢出后的数会以2^(8*sizeof(type))作模运算”,也就是说,如果一个unsigned char(1字符,8bits)溢出了,会把溢出的值与256求模。这里面1111 1111 +1 ==0了,然后取模。

对于有符号数而言,其处理和编译器有关。

—————————————————————————————————————————————————

浮点数:

(例子:float:0xC0A ,这个浮点数就可表示为:((-1)^1 )*1.25* (2^2),这是因为我们把他话为32位二进制,再按照1.8.23来斩断,分开算出E,S,M部分就行;在这个例子中,首位s=1,说明是个负数)

(阶码的值:E=e-Bias,BIas单精度是127,双精度是1023,e是unsigned值,题中e=128+1=129,bias=1023)

(而尾码M:frac有23位,首位大都是1(除非阶码的八位全零或者全一则置零) ,那么M=1.(后面23位),算出来是多少就是多少)

(所以最后表示V=-5)

作业2.47

 

————————————————————————————————————————————csapp第二章作业思路:

1.编写一段程序,来判断这个机器是小端(返回1)还是大端(返回0)

#include <stdio.h>
//dev-c++
int is_little_endian(){
    int a = 1;
    printf("%d %d",sizeof(int),sizeof(char));//帮助理解
    return *((char*)&a);
} 
//大端上是00 00 00 01
//小端是  01 00 00 00 
//以上是0X

char占一个字节,int占四个字节。

2.


这个比较简单,主要考察如何获取某些位。

(x&0xFF) | (y&~0xFF)

在写这个的时候遇到了点障碍,这里我也分享一下我的心路历程:一个字节有八个位,所以可以放入两个八进制数的位进去,所以是最低两位由x提供,其余位由y提供。

3.

i << 3表示i * 2^3, 其原理是找出需要替换的那些位,然后将其替换掉就行了。

unsigned replace_byte(unsigned x, unsigned char b, int i)
{
    return (x & ~(0xFF<<(i<<3))) | (b << (i<<3));
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值