计算机系统实验二:数据的存储与运算

参考教材:计算机系统基础 第二版 袁春风 机械工业出版社
参考慕课:计算机系统基础(四):编程与调试实践 https://www.icourse163.org/learn/NJU-1449521162

计算机系统实验导航

实验一:环境安装 https://blog.csdn.net/weixin_46291251/article/details/122477054

实验二:数据的存储与运算 https://blog.csdn.net/weixin_46291251/article/details/122478255

实验三:程序的机器级表示 https://blog.csdn.net/weixin_46291251/article/details/122478979

实验四:二进制程序逆向工程 https://blog.csdn.net/weixin_46291251/article/details/122479554

实验五:缓冲区溢出攻击 https://blog.csdn.net/weixin_46291251/article/details/122479798

实验六:程序的链接 https://blog.csdn.net/weixin_46291251/article/details/122480049

实验源码: xxx

准备

实验内容:

1 熟悉基本汇编语言程序;
2 不同类型数据在计算机的编码、存储、转换,整型数据加减运算及其计算机底层实现,浮
点数据的表示与运算。

实验目标:

1 理解计算机中数据的表示、存储和运算,熟悉程序的机器级表示;
2 学习和掌握程序的调试方法,强化计算机编程实践能力;
3 掌握 C 语言中位操作语句的使用。

实验任务:

1 学习 MOOC 内容
https://www.icourse163.org/learn/NJU-1449521162

  • 第三周 数据的存储与运算
    第 1 讲 真值与机器数
    第 2 讲 数据的宽度与存储
    第 3 讲 数据类型的转换
    第 4 讲 整数加减运算
    第 5 讲 浮点数的表示和运算
  • 第四周 程序的机器级表示
    第 1 讲 传送指令
    第 2 讲 加减运算指令
    第 3 讲 整数乘法指令

2 完成作业

题目一:反汇编解释程序

程序代码和注释说明

1.#include “stdio.h”
2.void main()
3.{ 
4.int ai=100, bi=2147483648, ci=-100;
5.unsigned au=100, bu=2147483648, cu=-100;
6.printf(“ai =%d, bi=%d, ci=%d\n”, ai, bi, ci);
7.printf(“au =%d, bu=%d, cu=%d\n”, au, bu, cu);
8.}

实验结果记录
对程序进行编译、反汇编、运行得到的结果如下:

在这里插入图片描述

执行程序时。ai bi ci 都是非静态局部变量,执行程序时被放在栈帧中。100, 2147483648, -100;可看作程序中的常数,把这些常数赋值给这些整形变量,并且放在栈帧中时,编译程序就把这些常数,直接编码在机器指令中

在这里插入图片描述

编译程序对这些常数进行编码的方法 :

在这里插入图片描述

编译时已经将这些常数编码在指令中,执行这些指令时就把这些数据直接复制给变量。
在这里插入图片描述
在这里插入图片描述

进入gdb

根据函数名设置断点在main函数处,运行程序。
在这里插入图片描述
在这里插入图片描述

R[ebp]- R[esp]+4=44
由于int占4个字节所以:44/4=11

在这里插入图片描述

结果分析与讨论

带符号整数bi的输出结果值为何是负数?

bi是int类型的数据,它的机器数是十六进制的0x80000000,程序中要求把bi输出为带符号中整数时,处理程序就把0x80000000当作补码数转换成真值,最高符号位由于为1,所以转换后的数是负数。

无符号整数cu,赋值一个负的数据后,cu输出的结果为什么会是这个值?

cu是无符号整数,cu的机器数是十六进制的0xffffff9c,当程序中要求cu按照无符号整数输出时,处理程序就把0xffffff9c每一位都当作数值位来处理,cu输出的值为429496719。

ai和ci他们的值一个是100一个-100,从真值的角度讲他们只差一个符号位,但他们在计算机内部的机器数差异却很大,ai的机器数是0x00000064,ci的机器数是0xffffff9c,这是因为补码对正数和复数的编码值的差异。

cu在计算机中实际存储的内容是什么?

Cu把-100的编码0xffffff9c赋值给了cu,所以cu的机器数是0xffffff9c,cu输出的真值是0xffffff9c的二进制值。

在这里插入图片描述

题目二:回答以下问题

C 语言程序如下,代码运行过程中各变量存储的机器数分别是什么?i1 和 i2 的值是否相同?f1 和 f2 的值是否相同?利用反汇编程序代码对结果进行解释说明。
程序代码和注释说明

1.#include “stdio.h”
2.int main()
3.{
4.int i1=0x7fffffff, i2, itemp; 
5.float f1=0x987654321, f2, ftemp;
6.ftemp=i1;
7.i2=ftemp;
8.itemp=f1;
9.f2=itemp;
10.printf(“i1=%d, i2=%d, f1=%f, f2=%f\n”, i1, i2, f1, f2);
11.}

实验结果记录

对程序进行编译、反汇编、运行得到的结果如下:

在这里插入图片描述

查看当前栈帧内容

在这里插入图片描述

上图可得出i1和i2的机器数,并不相同。

结果分析与讨论

i1的机器数为0x7fffffff,为了转换成float类型的浮点数,要把i1写成尾数和阶码的形式,尾数的有效数字有31位,而float格式的浮点数的有效数字只有24位,所以需要对尾数进行舍入,舍去尾部的7位,然后根据IEEE754的舍入原则,这里执行入的操作,所以在有效数字最低位加1,有效数字就变成了10B,但是这又不符合规格化的尾数了,所以需要把尾数调整为1.0B,阶码需要加1,所以阶码为31,最终i1转化成的浮点数为:符号位为0阶码为31+127尾数为23个0。要把这个浮点数赋值给i2,又要进行浮点格式和补码的转换,这个浮点数的真值就是1.0*2^31(1后面跟31个0),所以这就是i2的机器数,i1与i2机器数不同的原因就是在i1转化成浮点数时进行了近似处理。从机器数的编码来看,i2的编码比i1的编码大1,但是将i2的机器数还原为真值后,i2的真值为负数,与i1的真值差异较大。如下图所示:
在这里插入图片描述

由上述反汇编得出的代码可知,f1的机器数是16进制的0x51187654。
f1的初始值是0x987654321用二进制编码有36个二进制位,超过了float浮点数的24位精度,所以这里要进行舍操作,f1的浮点数的编码是符号位为0阶码为35+127尾数是00110000111011001010100三十二位二进制(f1的机器数),f1转换为int类型时需要做编码格式上的转换,f1的机器数对初始数据做过舍操作,所以f1的真值相对于初始值f1的二进制依旧有36位,但是它的低12位全为0,int类型有32位而f1有36位,超过了int的表示范围,f1转换为itemp时溢出。
f2与f1不一样的根本原因是f1超出了int的表示范围,转换为int时,系统赋值最高位为1其余位为1
在这里插入图片描述

题目三:分析运行过程中寄存器 eax,ebx,ecx 中的值

程序代码和注释说明

1.#include “stdio.h”
2.void main()
3.{
4.int p[2]={0x12345678,0x11223344};
5.asm
6.(
7.“lea -0x14(%ebp),%eax\n\t”
8.“mov -0x14(%ebp),%ebx\n\t”
9.“mov $1,%ecx\n\t”
10.“lea -0x14(%ebp,%ecx,4),%eax\n\t”
11.“mov -0x14(%ebp,%ecx,4),%ebx\n\t”
12.);
13.printf(“understand mov and lea\n”);
14.}

运行程序:
在这里插入图片描述

源代码含义:
首先创建一个大小为2的int类型数组
然后在代码中嵌入汇编语句,其中:

//ebp-10是p0的地址
"lea -0x10(%ebp),%eax\n\t" //将p0的地址赋值给eax
"mov -0x10(%ebp),%ebx\n\t" //将p0地址的值赋值给ebx
"mov $1,%ecx\n\t"  //ecx相当于P[i]的下标i
//ebp-10 + ecx*4是p1的地址, int占4个字节
"lea -0x10(%ebp,%ecx,4),%eax\n\t" //p1的地址
"mov -0x10(%ebp,%ecx,4),%ebx\n\t" //p1地址上存储的数据

实验结果记录

先对源代码进行编译和反汇编,打开反汇编得到的代码,如下:
在这里插入图片描述

从中可以看出P0被放在-0x10(%ebp)的位置,p1被放在-0xc(%ebp)的位置。
修改后的源代码:
在这里插入图片描述

然后打开gdb调试
在这里插入图片描述

输出当前的栈帧:
在这里插入图片描述

执行两个汇编语句,然后输出eax和ebx的值:

在这里插入图片描述

执行三个汇编语句,然后输出eax和ebx的值:
在这里插入图片描述

结果分析与讨论

由上述调试结果不难看出:
执行完前两个汇编语句后,lea指令将p0的地址传送给了eax寄存器,而mov指令将p0 的数值赋值给了ebx寄存器。
然后根据首地址和长度计算出p1的地址,再用同样的方法处理p1,得到的结果依然是eax接收p1的地址,ebx接收p1的值.

所以得出结论:Lea指令实现的是地址传送,mov实现的是数据传送。

题目四:只用运算符~和|来实现位的与操作函数:

1.int bitAnd(int x, int y)
2.例如:bitAnd(6, 5) = 4

程序代码和注释说明

1.# include<stdio.h>
2.int bitAnd(int x, int y){
3. return ~ (~x | ~y);
4.}
5.
6.void main(){
7.
8. int ans = bitAnd(6,5);
9. printf("%d\n",ans);
10.}

思路:
要利用运算符~和|来实现bitand的功能,只需要将两个操作数先分别取反,然后做或操作,结果再取反即可。
即 a&b == a | ~b )

实验结果记录

查看程序内容:
在这里插入图片描述

生成可执行文件并运行程序:
在这里插入图片描述

可见程序结果满足需求。

题目五:只用运算符! ~ & ^ | + << >>实现比较 x 和 y 的大小的函数:

1.int isLessOrEqual(int x, int y)
2.例如:isLessOrEqual(4, 5) = 1

程序代码和注释说明

1.# include<stdio.h>
2.int isLessOrEqual(int a,int b) {
3.  int diff = a ^ b;
4.  if (!diff) return 1; //a = b 
5.  
6.  // 001xxxxx -> 00100000
7.  diff |= diff >> 1;
8.  diff |= diff >> 2;
9.  diff |= diff >> 4;
10.  diff |= diff >> 8;
11.  diff |= diff >> 16;
12.  diff ^= diff >> 1;
13.  
14.  if (!(a & diff))
15.   return 1; //a < b
16.  else
17.   return 0; //a > b
18.
19.}
20.void main(){
21.
22. //int a=1,b=5;
23. int ans = isLessOrEqual(4,5);
24. printf("%d\n",ans);
25.
26.}

比较的原理: 只需要找出第一个从最高位开始找出第一个不同的Bit,这一位是1的数是较大者。

实验结果记录

生成可执行文件并运行程序:
在这里插入图片描述
可见程序结果满足需求。

posted @ 2022-03-05 15:59  Cheney822  阅读(663)  评论(0编辑  收藏  举报