HNU君陌
此为本人在大二下学期准备深入理解计算机系统时于闲暇时间所创,里面有部分本人自己的理解,仅供参考。此系统包含c++代码和八个txt文件,支持dev编译运行。
c++主程序源码:
#include
#include
#include
using namespace std;
int main()
{
ifstream ou;
string a,b;
system("color F0");
cout<<"******************************"<<endl;
cout<<" 欢迎来到计算机系统复习系统\n"<<endl;
cout<<" 制作人:方寸谛"<<endl;
cout<<" 制作时间:2019.4.21"<<endl;
cout<<"******************************"<<endl;
Sleep(1000);
A:
system("cls");
cout<<"请选择复习模块:"<<endl;
cout<<"1、信息的表示和处理"<<endl;
cout<<"2、整数表示"<<endl;
cout<<"3、整数运算"<<endl;
cout<<"4、浮点数表示"<<endl;
cout<<"5、汇编语言"<<endl;
cout<<"6、翻转、分支、循环"<<endl;
cout<<"7、过程"<<endl;
cout<<"8、数组的分配和访问"<<endl;
cout<<"0、退出系统"<<endl;
int n;
cin>>n;
system("cls");
switch(n){
case 1:{
ou.open("1.txt");
break;
}
case 2:{
ou.open("2.txt");
break;
}
case 3:{
ou.open("3.txt");
break;
}
case 4:{
ou.open("4.txt");
break;
}
case 5:{
ou.open("5.txt");
break;
}
case 6:{
ou.open("6.txt");
break;
}
case 7:{
ou.open("7.txt");
break;
}
case 8:{
ou.open("8.txt");
break;
}
case 0:{
cout<<"******************************"<<endl;
cout<<" 感谢使用本系统,祝愿考试顺利\n"<<endl;
cout<<" 制作人:方寸谛"<<endl;
cout<<" 制作时间:2019.4.21"<<endl;
cout<<"******************************"<<endl;
Sleep(1000);
return 0;
break;
}
}
ou>>a;
while(a!="0"){
ou>>b;
if(a=="*") cout<<" "<<b<<endl<<endl;
else cout<<a<<" "<<b<<endl<<endl;
ou>>a;
Sleep(100);
}
ou.close();
system("pause");
goto A;
}
1.txt文件:
1 信息的表示方法有2进制和16进制,注意两者之间的转换,2->16可以每四位取16进制;16->2可每位分四
2 字长指明整数和指针数据的标称大小,决定的最重要的系统参数就是虚拟地址空间的最大大小,比方说一个字长为w的机器,虚拟地址的范围为0~2^w-1,程序最多访问2^w个字节
3 在几乎所有的机器上,多字节对象被存储在连续的字节序列,对象的地址为所使用字节中最小的地址
4 小端法:在存储器中按从最低到最高的顺序存储对象,如1234存储为:34-12,地址从小到大
5 大端法:在存储器中按从最高到最低的顺序存储对象,如1234存储为:12-34,地址从小到大
6 反汇编器:一种确定可执行程序文件所表示的指令序列的工具
7 字符由某个标准编码表示,常见的是ASCII码,如1234得到31,32,33,34,注意终止字节的十六进制数为0x00
8 因为ASCII码作为字符码任意平台结果相同,与字节顺序和字节大小规则无关,故文本数据比二进制有更强的平台独立性
9 二进制代码是不兼容的,很少能在不同机器和操作系统组合间一直,指令编码即为一种二进制代码,不同机器采用不同且不兼容的指令和编码方式
10 注意注意!!!从机器角度来看,程序仅仅是字节序列
11 c中支持位级运算(最常见的是掩码运算),逻辑运算(如果对第一个参数求值能确定T或F,则不会对第二个参数进行求值),移位运算,其中注意算术右移用最高位补齐,逻辑右移用0补齐
0
2.txt文件:
1 唯一一个与及其相关的取值范围是大小指示符long,大多数64用8字节,32用4字节
2 注意对于有符号数,其负数的范围比正数大1(因为有0)
3 无符号数的编码通过B2Uw表示,如B2U4([0001])=0*2^3+0*2^2+0*2^1+1*2^0,在图中用长度为2^i的箭头表示每个位的位置i,对应的数值为所有值为1的位对应的长度之和:B2Uw(x)=x0*2^0+...+xw-1*2^w-1
4 补码编码,将字的最高有效位作为负权:B2Tw(x)=-xw-1*2^w-1+x0*2^0+...+xw-2*2^w-2,用图中向左指的灰条与向右的蓝条加起来决定,能表示的最小值为[10...0],最大值为[01...1],不对称
* 反码:最高有效位权为-(2^w-1-1),相当于完全相反
* 原码:最高有效位为符号位,相当于部分相反
* 补码:反码+1
5 强制类型转换的结果保持机器代码层面的位模式不变
6 从有符号到无符号,图上将负权值灰条反转并入
7 从无符号到有符号,小的数将保留原值,大的将会转换为负值
8 注意注意!!!当执行一个运算的时候,若他的一个数为有符号数,一个为无符号数,将会隐式地将有符号数强制转换为无符号数进行运算,并假设均非负,对>或<有影响
9 将一个无符号数转换为一个更大的无符号数,只要简单在开头添0即可,称为0扩展
10 将一个补码数字转换为一个更大的可执行符号拓展,即添加最高有效位的值
11 截断数字:将会直接抛弃高位,可能会改变他的值,是溢出的一种形式
0
3.txt文件:
1 无符号加法等价于将结果模上2^w,如1001与1100相加有10101模10000有0101,会产生字长膨胀,产生溢出
2 溢出指完整的整数结果不能放到数据类型的字长限制中
3 补码加法考虑太大或太小的情况,当太大的时候,为正溢出,结果为负;当太小的时候,为负溢出,结果为正中间部分一样取模
4 补码的非运算可对每一位取反再加1,或者找到从右往左数的第一个1,将该1的左侧数全部取反,如1100->0100
5 无符号乘法可能需要2^w位表示,所以将结果直接取模
6 补码乘法的范围是-2^2*w-2+2^w-1~2^2*w-2,通过将2w位的乘积截断为w位方式实现
7 乘法相当慢,需要10个时钟,可以用一位与加法得到常数因子的乘法,左移一位相当于乘2
8 除以2的幂更慢,需要30个时钟,分别通过逻辑右移和算术右移达到目的,整数除法总是舍入到0
0
4.txt文件:
1 浮点数表示形如V=x*2^y的有理数进行编码,二进制小数点左移一位相当于除以2,增加二进制的长度可以提高精度
2 IEEE浮点标准用V=(-1)^s*m*2^e表示一个数,其中s为符号位表示正负;m为尾数,表示精度;e为阶数,给浮点数加权,权重为2的e次幂(可负),所以浮点数的位包含三个字段:1个单独的符号位、k位的阶码字段和n位的小数字段
3 单精度格式中s=1,k=8,n=23;双精度格式中s=1,k=11,n=52
4 规格化数中阶码字段不为0,计算时为:(f(小数字段)+1)*2^(E(阶码字段)-(2^k-1-1))
5 非规格化数中阶码字段为0,计算时为:f(小数字段)*2^(E(阶码字段)-(2^k-1-1))
6 浮点数的0有+0和-0之分,当阶码字段全为1且小数字段不为0的时候是NaN表示不是一个数;当阶码字段全为1且小数字段全为0的时候是无穷大,可以表示正无穷和负无穷
7 浮点运算只能近似的标识实数运算,需要进行舍入。有向零舍入(把正数向下舍入,负数向上舍入,得到|x'|<|x|)、向下舍入(把正数负数均向下舍入)、向上舍入(把正数负数均向上舍入)、向偶数舍入(将数字向上或向下舍入,使最低有效位的数字是偶数)
* 向偶数舍入:例如有效数字超出规定数位的多余数字是1001,他大于超出规定最低位的一般,故最低位进1,如果多余数字是0111,他小于最低位的一般,则舍掉多余数字。对于多余数字1000,如果最低位为0则舍掉多余位,最低位为1则进位1,使最低位仍为0
8 注意注意!!!向偶数舍入仅仅在多余数字是恰好一半的时候起作用,否则一样是四舍六入
9 移码是符号位取反的补码,一般用指数的译码-1来做浮点数的阶码
10 一般用原码来表示阶数,然后-(2^(w-1)-1)
11 关于溢出:
* 若运算结果的双符号位为00, 表示结果为正数,无溢出;
* 若运算结果的双符号位为11,表示结果为负数,无溢出;
* 若运算结果的双符号位为10,表示负溢出。
* 若运算结果的双符号位为01,表示正溢出。
0
5.txt文件:
1 word字表示16位数据,doubleword双字表示32位数据,quadword四字为64位数据
2 大多数常用数据类型以双字形式存储,所有的指针都存为4字节的双字
3 大多数汇编指令都有一个字符后缀,表示操作数的大小,b为字节(8位)、w为字(16位)、l为双字(32位)
4 eax为累加寄存器,ecx为计数寄存器,edx为数据寄存器,ebx为数据寄存器,esi为源指针寄存器,edi为目的指针寄存器
* eax为32位,其中的ax为16位,再其中的ah和al为8位
5 操作数指示符有三类,立即数(加上$前缀)、寄存器(加上%前缀)、存储器(用小括号+地址表示)
6 有多种不同的寻址模式,允许不同形式的存储器引用,最常用语法形式为:lmm(eb,ei,s),其中lmm为立即数偏移lmm、eb为基址寄存器、ei为变址寄存器、s为比例因子,s必须是1,2,4,8,有效地址被计算为lmm+(eb)+(ei)*s
7 数据传送指令movs,d作用是将s给d,具体如下:
* mov:简单移动(大小相等)
* movs:符号拓展,用符号位填充不足(小到大)
* movz:零拓展,用0填充不足(小到大)
* cmov:先比较,满足再进行移动
* cbw:从字节拓展到字,符号位拓展
* movabsq:是新增的指令,直接将一个64位的立即数放到一个64位的寄存器中
* pushl:双字压入栈,esp值-4,push则是-2
* popl:双字出栈,esp值+4,pop则是+2
8 算术和逻辑操作xxxs,d作用是将结果给d,具体如下:
* leal:加载有效地址,将s作为地址取值放入d中
* inc:加一
* dec:减一
* neg:取负
* not:取补
* add:相加
* sub:相减
* imul:乘
* xor:异或
* or:或
* and:与
* sal:左移
* shl:左移(等于sal)
* sar:算术右移
* shr:逻辑右移
* test:相当于与,但不改变数据,仅改变条件码
9 条件码,cpu中一组单个位的条件码寄存器,其描述了最近的算术或逻辑操作属性:
* cf:进位标志,可用来检查溢出,1有0无
* zf:零标志,最近的操作得出结果为0,0则为1
* of:溢出标志,最近的操作导致一个补码溢出,1有0无
* sf:正负标志,最近运算的结果符号是正是负
* pf:判断结果中二进制1的个数的奇偶性,偶正奇负
* df:方向标志
* af:辅助,在字节操作时低半字节向高半字节进位或借位,1有0无
* if:终端,决定cpu是否响应外界可屏蔽中断请求,IF为1是允许相应
* tf:陷阱,被设置为1时cpu进入单步模式
* cx:loop指令循环计数器
10 跳转指令,有直接跳转和间接跳转,jmp*%eax是直接跳转,eax中存储地址,直接跳到该地址;jmp*(%eax)是间接跳转,eax存储存储器(内存)中的地址,存储器存储目的地址,先读出存地址,再找到目的地址,再跳转,具体如下:
* cmp:比较指令,相当于减法指令,对ZF,CF寄存器进行改变
* jz/je:相等,根据ZF=1跳转
* jnz/jne:不相等,根据ZF=0跳转
* js:是负数,根据SF=0跳转
* jns:非负数,根据SF=1跳转
* jg:大于
* jge:大于等于
* jl:小于
* jle:小于等于
* ja:超过,根据CF=0且ZF=0跳转
* jae:不低于,根据CF=1且ZF=1跳转
* jb:低于,根据CF=1跳转
* jbe:不超过,根据CF=0跳转
11 对于cmpAB会比较B与A,如ja表示判断B是否超过A
12 物理地址的形成过程是将短地址左移4位加偏移地址
0
6.txt文件
1 先将c语言转换到goto形式的语言再转换到汇编语言代码,通过goto来代替if条件语句
2 大多数汇编器通过do-while循环来产生循环代码,即便其他的循环也是如此,都需要转换为do-我hi饿了循环,每次循环进行测试,然后比较终止条件与当前条件,最终得到结果
3 条件传送指令,数据的条件转移是一种替代,先计算一个条件的两种结果,然后根据条件对结果进行选择,基于条件数据传送比基于条件控制转移性能好
4 switch语句将会在满足的情况下生成一个跳转表,跳转表是一个数组,表项i是一个代码段的地址,这个代码段实现当开关索引值等于i时程序应采取的动作,程序代码用开关索引值来执行一个跳转表内的数值引用,确定跳转指令的目标
5 跳转表的优点是执行开关语句的时间与开关情况的数量无关,当开关情况数量比较多,且值得范围比较小时使用跳转表
* 执行switch语句的关键是通过跳转表访问代码位置
6 在引用跳转表的时候jmp指令的操作数有前缀*,表明是一个间接跳转,操作数指定一个寄存器的位置,索引由寄存器的值给出
7 注意注意!!!对于跳转表中的重复情况是简单地使用同样的代码标号,对于缺失的情况是使用默认情况的标号
0
7.txt文件
1 一个过程调用包含将数据(以过程参数和返回值的形式)和控制从代码的一部分传递到另一部分;另外他必须在进入时为过程的局部变量分配空间,并在退出时释放空间,数据、局部变量的分配和释放通过栈实现
2 机器用栈来传递过程参数、存储返回信息、保存寄存器用于以后恢复、本地存储、为单个过程分配的部分栈为栈帧
3 栈向低地址方向增长,而栈指针esp指向栈顶元素,利用push将数据压入栈,用pop取出,将栈指针的值减少适当值可以分配无初始值的空间,增加可释放空间
4 通过call指令调用函数,分为直接调用或间接调用,直接调用将会调用函数,间接调用将会调用地址,leave为返回准备栈,ret从过程调用中返回,返回到call指令后的那条指令
* eax、edx、ecx为调用者保存寄存器,调用函数可以覆盖这些寄存器,而不会破坏主过程的数据;ebx、esi、edi为被调用者保存寄存器,调用函数在使用时需要先将其中的数据保存到栈,并在返回前恢复;ebp和esp必须保持
* 再返回前,函数必须将栈恢复到原始,可以恢复所有的被调用者保存寄存器与ebp,并充值esp使其指向返回地址
0
8.txt文件
1 对于T-A[N],首先在存储器中分配一个L*N字节的区域,L为数据类型T的大小(字节),用Xa来表示起始位置;引入标识符A,可用A作为指向数组开头的指针,值是Xa;可用从0~n-1之间的整数索引来访问数组元素,数组元素i放在地址为Xa+L*i的地方
2 如果数组A[n],将A的地址存放在edx中,i存放在ecx中,那么指令:movl(%edx,%ecx,4),%eax会计算Xa+4*i,将结果作为地址读出在存储器中的值,并将结果放到eax中
3 对指针运算,计算出来的值会根据引用的数据类型的大小进行伸缩
* leal指令用来产生地址,movl用来引用存储器
4 int-A[5][3]等价于:typedef-int-row_t[3]和row_t-A[5],其中row_t为一个三个数的数组,A中包含5个这样的数组,每个要12个字节,总共60个字节;在存储中会以数组起始为基地址,偏移量为索引,产生计算期望的元素的偏移量,如T-D[R][C],其元素D[i][j]地址为&D[i][j]=Xd+L*(C*i+j)
5 注意注意!!!在存储器中二维数组按照行优先排列
6 定长数组:当值访问第i行的元素,会创建一个局部指针变量AW,提供对第i行的直接访问,AW被初始化为&A[i][0],可以通过AW[j]访问A[i][j],所以“先列后行”比“先行后列”更快
7 变长数组:允许数组的维度是表达式,在数组被分配时才计算出来;引用变长数组只需要在定长数组的基础上用乘法指令对i伸展n倍,而不能用一系列的移位和加法
8 结构体的所有组成部分都存放在存储器中一段连续的区域内,指向结构的指针就是结构体的第一个字节的地址,如果元素中有数组,那么数组会改为内嵌,为了访问要将结构的地址加上适当的偏移
9 联合允许以多种类型来一用一个对象,即机器代码不改变,而以不同的数据类型来引用解释,总的大小等于最大字段的大小,证明机器代码没有类型信息,所以当用联合将两个不同大小的数据结合时,字节顺序就很重要
10 数据对齐:要求某种类型对象的地址必须为某个K的倍数,如设计一个处理器总是从存储器中取出8个字节,则地址必须为8的倍数,可能去一个对象需要执行两次存储器访问,因为对象可能在两个8字节存储器块中,编译器中用.align4指明全局数据所需的对其(K=4)所以char尽管本身只有1字节,却占据了4字节的空间
11 指针不是机器代码的一部分,而是C语言提供的一种抽象,避免寻址错误,指针是从一种类型强制转换为另一类型,只改变类型不改变值
0
由于直接上传文件会收费,这里就上传了代码,希望湖大的学弟学妹理解这么长的原因。