信息存储:
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));
}