PA1--实现基础设施、表达式求值和监视点
PA1实验总结
- 基础设施
- PA1.1基础设施
- 单步执行
- 打印寄存器
- PA1.2 表达式求值
- 1. 词法分析(识别token)
- 2. 递归求值
- PA1.3实现监视点
基础设施
在实现基础实施前需要的是进行源码的阅读,并且实现寄存器的模拟。实验中已经给了提示,如果需要实现寄存器的模拟需要掌握什么是匿名。只有在了解了什么是union后才可以实现寄存器的模拟,下附实现的代码(代码永远不唯一)。
typedef struct {union{union{ uint32_t _32; uint16_t _16;uint8_t _8[2];} gpr[8];/* Do NOT change the order of the GPRs' definitions. *//* In NEMU, rtlreg_t is exactly uint32_t. This makes RTL instructions1. in PA2 able to directly access these registers.*/ struct{uint32_t eax;uint32_t ecx;uint32_t edx;uint32_t ebx;uint32_t esp;uint32_t ebp;uint32_t esi;uint32_t edi;}; }; vaddr_t eip;} CPU_state;
在实现了寄存器模拟后,开始进行基础设施的搭建。
PA1.1基础设施
单步执行
实现单步执行的功能有两个关键点:
- 学习strtok()函数。直接去查找库函数的官方文档即可。
- 调用cpu_exec()函数。
static int cmd_si(char *args){ char *arg = strtok(args," ");// printf("%s\n",arg);if(arg == NULL){printf("too few arguments.\n");return 1;}int num = atoi(arg);cpu_exec(num);printf("OK");return 0;
};
注:附上的代码注释部分是用来测试用的。
打印寄存器
打印寄存器部分比较容易实现,只需要用strtok()函数将字符串分割成想要的部分即可。
static int cmd_info(char *args){char *arg = strtok(args," ");printf("%s\n",arg);//cpu infoif (strcmp(arg,"r")==0){printf("eax is %x\n",cpu.eax);printf("ecx is %x\n",cpu.ecx);printf("edx is %x\n",cpu.edx);printf("ebx is %x\n",cpu.ebx);printf("esp is %x\n",cpu.esp);printf("ebp is %x\n",cpu.ebp); printf("esi is %x\n",cpu.esi);printf("edi is %x\n",cpu.edi);printf("---------------------------\n");}else if(strcmp(arg,"w")==0){print_wp(); //此部分是后期用来打印监测点状态使用,前期可以先注释掉。}return 0;
}
### 扫描内存
这部分直接给出代码,没有什么特别的思考逻辑。
static int cmd_x(char *args){//获取内存起始地址和扫描长度。if(args == NULL){printf("too few parameter! \n");return 1;}char *arg = strtok(args," ");if(arg == NULL){printf("too few parameter! \n");return 1;}int n = atoi(arg);char *EXPR = strtok(NULL," ");if(EXPR == NULL){ printf("too few parameter! \n");return 1;}if(strtok(NULL," ")!=NULL){printf("too many parameter! \n");return 1;}bool success = true;//vaddr_t addr = expr(EXPR , &success);if (success!=true){printf("ERRO!!\n");return 1;}char *str;// vaddr_t addr = atoi(EXPR);vaddr_t addr = strtol( EXPR,&str,16 );// printf("%#lX\n",ad);//进行内存扫描,每次四个字节;for(int i = 0 ; i < n ; i++){uint32_t data = vaddr_read(addr + i * 4,4);printf("0x%08x " , addr + i * 4 );for(int j =0 ; j < 4 ; j++){printf("0x%02x " , data & 0xff);data = data >> 8 ;}printf("\n");}return 0;
}
PA1.2 表达式求值
这个部分我用了比较长的时间,而且在做后面的实验时又对这个部分进行了补充。对于这部分,我不会贴出全部源代码,只会给出实验结果和思路。
整个表达式求值的过程我分成了两个部分:
1. 词法分析(识别token)
在进行此法分析时,首先要进行的是了解正则表达式。正则表达式的用途很广,而且很神奇,最重要的是还很复杂。在此我大概说一下自己在实现这个部分是走过的坑。
第一个坑毫无疑问是正则表达式的撰写,对于这个我想说的是:多测试,尽可能想的全面。只有这样才能写出一个可以使用的正则表达式。
在完成tokens识别后需要做的是将token信息存储到tokens[]数组中,这里我使用的是strncpy()函数,因为这个函数可以更好的控制字符串复制的起始位置。
至此就已经实现一个简单的词法分析器了。
2. 递归求值
在识别并记录下全部的token信息后,可以进行求值运算了,在整个求值运算中已经有了一个完整的递归框架,这里我会详细说下其中两个功能函数
- 括号匹配函数
bool check_parentheses(int p ,int q){// printf("--------------\n"); int i,tag = 0;if(tokens[p].type != TK_LEFT || tokens[q].type != TK_RIGHT) return false; //首尾没有()则为false for(i = p ; i <= q ; i ++){ if(tokens[i].type == TK_LEFT) tag++;else if(tokens[i].type == TK_RIGHT) tag--;if(tag == 0 && i < q) return false ; //(3+4)*(5+3) 返回false} if( tag != 0 ) return false; return true;
}
- 主操作符寻找函数
int dominant_operator(int p , int q){int i ,dom = p, left_n = 0;int pr = -1 ;for(i = p ; i <= q ; i++){if(tokens[i].type == TK_LEFT){left_n += 1;i++;while(1){if(tokens[i].type == TK_LEFT) left_n += 1;else if(tokens[i].type == TK_RIGHT) left_n --;i++;if(left_n == 0)break;} if(i > q)break;} else if(tokens[i].type == TK_NUM10) continue;else if(pir(tokens[i].type ) > pr){pr = pir(tokens[i].type);dom = i;} } // printf("%d\n",left_n);return dom;
}
其中pir是优先级函数,这里我按照c语言的标准对不同操作符赋予优先级。
在完成以上函数后,就已经实现了基本的表达式求值函数,接下来是对表达式求值的扩展- -负数和指针的识别。
for(int i = 0 ;i<nr_token;i++) {if(tokens[i].type=='*'&&(i==0||(tokens[i-1].type!=TK_NUM10&&tokens[i-1].type!=TK_LEFT&&tokens[i].type!=TK_NUM16)))tokens[i].type = TK_POINT;if(tokens[i].type=='-'&&(i==0||(tokens[i-1].type!=TK_NUM10&&tokens[i-1].type!=TK_LEFT&&tokens[i].type!=TK_NUM16)))tokens[i].type = TK_NEG;}
在进行负数和指针的识别时要注意特殊情况 多个负号和多个指针,这里我在处理时加了一下代码:
if(tokens[op].type == TK_NEG){for( i = op ; i<nr_token ; i++){if(tokens[i].type == TK_NUM10){sscanf(tokens[i].str, "%x", &result);//printf("%d \n",result);// return -result;break;}} for( ;i > 0 ;i --) result = -result;return result;} else if (tokens[op].type == TK_POINT){for( i = op ; i<nr_token ; i++){if(tokens[i].type == TK_NUM10){sscanf(tokens[op+1].str, "%x", &result);// result = vaddr_read(result, 4);// return result;break;}} for( ;i > 0 ;i -- ) vaddr_read(result, 4);return result;}
这里的逻辑是统计符号的个数,然后进行相应次数的操作。
至此完成PA1.2部分。
PA1.3实现监视点
对于实现监视点功能主要有三个部分函数(监视点所需要的结构类型,大家可以自行思考,能够实现功能即可):
- 添加监视点:
//需要存入表达式和结果。
WP *new_wp(char *str , int value){if(su == true){init_wp_pool();su = false;//printf("!!!!!!!!!!!!!\n");}if(free_ == NULL){printf("Erro!free is null.\n");assert(0);}WP *new = NULL;new = free_;free_ = free_->next;// printf("!!!!%d\n",value);// printf("!!!!%s\n",str);new->value = value;// printf("!!!!%d\n",new->value);strcpy(new->expr, str);// printf("!!!!%d\n",new->value);// printf("%s /n",new->expr);new->next = NULL;new->isused = true;if(head == NULL) head = new;else{new->next = head;head = new ;}return new;
}
再添加监视点前,我进行了一次判断,这个判断主要用途是判定监视点池是否完成初始化。
3. 释放监视点
void free_wp(int no){WP *p = head;if(head == NULL){ printf("监视点列表为空。 \n");assert(0);}else if(p->NO == no){head = head->next;p->value = 0;p->isused = false; p->next = free_;free_ = p;printf("已经删除第%d个监视点。\n", no);// free(p);return;}else{WP *q = head;p = p ->next;while(p!=NULL){if (p->NO == no){q->next = p->next;p->value = 0;p->isused = false;p->next = free_;free_ = p;printf("已经删除第%d个监视点。\n", no);// free(p);free(q);return;}else{p = p -> next;q = q -> next;}}printf("不存在第%d个监视点。\n",no);return;
}
- 打印监视点
void print_wp(){WP *p = head;if(p ==NULL){printf("监视点为空!\n");return;}else{while(p!=NULL){printf("%d %s 0x%08x\n",p->NO , p->expr, p->value);p=p->next;}return;}return;
}
至此已经完成了整个PA1实验,思考题每个人都会有自己的答案。
PA1--实现基础设施、表达式求值和监视点相关推荐
- 经典笔试上机考题-表达式求值
相信参加过笔试面试同学应当见到过表达式求值这道题,下面列举的一道经典的考题,本文将同大家一起细细探讨一下表达式求值这一类问题的求法,希望抛砖引玉,其中有不妥的地方也请大家多多批评指正. /* 功能:四 ...
- NOIP2013普及组 T2 表达式求值
OJ地址:洛谷P1981 CODEVS 3292 正常写法是用栈 1 #include<iostream> 2 #include<algorithm> 3 #include&l ...
- 栈 -- 顺序栈、链式栈的实现 及其应用(函数栈,表达式求值,括号匹配)
文章目录 实现 顺序栈实现 链式栈实现 应用 函数栈 的应用 表达式求值中 的应用 括号匹配中 的应用 我们使用浏览器的时候经常会用到前进.后退功能. 依次访问完一串页面 a – b – c之后点击后 ...
- 【Java】LeetCode 150. 逆波兰表达式求值 (后缀表达式)
题目: 根据 逆波兰表示法,求表达式的值. 有效的算符包括 +.-.*./ .每个运算对象可以是整数,也可以是另一个逆波兰表达式. 说明: 整数除法只保留整数部分. 给定逆波兰表达式总是有效的.换句话 ...
- C语言----表达式求值之隐式类型转换
前言: 表达式求值的顺序一部分是由操作符的优先级和结合性决定. 同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型,称为隐式类型转换,下面给出隐式类型转换的详细讲解,希望对大家有所帮助~ 隐 ...
- [数据结构]表达式求值
一.问题描述 表达式求值是数学中的一个基本问题,也是程序设计中的一个简单问题.我们所涉及的表达式中包含数字和符号,本实验中处理的符号包括'+'.'-'.'*'.'/'.'('和')',要求按照我们所习 ...
- c语言作业算术表达式求值,算术表达式求值演示(C语言版)
//头文件预处理命令 #include #include //----------函数结果状态代码----------------- #define TRUE 1 #define FALSE 0 #d ...
- 严蔚敏《数据结构》C语言版 第三章 算法3.4 表达式求值
这个我是看到华为今年招聘的机试题的第三题. 输入一串算术表达式的字符串,把它的结果算出来. 1 //表达式求值(输入.输出和中间结果均只能是0-9)运算符仅限+-*/. 2 #include < ...
- 函数嵌套 lisp表达式求值
问题 D: lisp表达式求值 时间限制: 1 Sec 内存限制: 128 MB 提交: 105 解决: 43 [提交][状态][讨论版] 题目描述 lisp是一种非常古老的计算机语言,是由约翰· ...
最新文章
- 即时编译和提前编译_即时编译说明
- Linux通过XAMPP集成软件包搭建LAMPP环境
- ORM(一)OQL结构图
- mysql存储过程或函数中传入参数与表字段名相同引发的悲剧
- 第一行代码学习笔记第六章——详解持久化技术
- fiddler和wireshark工具介绍及对比 - [测试技术知识]
- 给数据库表字段设置默认值
- Golang 大杀器之跟踪剖析 trace(转载)
- UE4 Light Functions(光源函数)
- 为什么你会觉得微服务架构很别扭
- Hadoop启动jobhistoryserver
- js—封装原生AJAX
- Bug Algorithms
- SSM毕设项目国有资产管理系统3c938(java+VUE+Mybatis+Maven+Mysql)
- “阀值”与“阈值”的区别
- Silverlight XAP文件运行器
- 手机插了sd卡显示无服务器,手机sd卡无法读取之sd卡无文件系统或文件系统不受支持怎么办...
- 微信小程序 9宫格翻牌动画
- 纯css实现的娃娃机web前端html页面源码
- 黑马程序员前端实战项目---PC端品优购(下)