数组越界检测
前言111
该文章用来记录一次简单的数组越界检测,查看实验手册
用到的知识仅有c++基础语法和两个简单的STL容器:vector和map以及一颗耐心
正文
实验的一些小理解
该实验主要可以分成两个部分:
- 一是数组和变量的定义(都存储在VarDelList中)
- 二是数组和变量的使用(都存储在ArrayUseList中)
- 注:grammartype我在此实验中并没有用到
实验的结构体解析
struct ArrExpr:
std::string id: 表示该数组的名称
int dimension:表示该数组的维度
std::vector<struct Expression*> length:
以我个人片面的理解来看,vector相当于一个加强版的数组,它可以在里面存各种类型的数据,比较好用
length[0]表示该数组的第一个维度的表达式。例如a[0],那么length[0]的值则为0
length[1]表示该数组的第二个维度的表达式。例如a(4)(5)(此处以()来代替[],避免关键符号),那么length[0]的值则为4,length[1]的值则为5
事实上,length[x]代表一个一个vector括号里的元素,即是struct Expression*
struct Expression:
int grammartype:不同语法的种类(目前没有发现有什么特别大的用处)
std::string digitsequence:表示该表达式内的纯数字,一看就明白吧。例如:a[5],digitsequence的值为” 5 “
==要注意,这里的值都是字符串类型的,需要使用stoi()函数把它变成整型的==
!!!stoi函数不能用在一个空的字符串上,否则会报错
std::string id: 表示该表达式内的变量名,一般是数组表达式里面的引用变量,例如a[n],id则等于n
std::string op:表示该表达式内的运算符号,例如:’ + ‘, ‘ - ‘, ‘ * ‘, ‘ / ‘;
srtuct ArrExpr*:表示该表达式有一个数组,例如:b[a[0]];
struct Expression* expr:表示该表达式内还有表达式,很好理解。一般此元素不会会单独出现,都会和其他的如数字和变量一起,例如:b[n + 65],那么后面这个数字65就是下一级的expression里的数字了;
当表达式内有运算符号时,此时就比较麻烦了,需要考虑各种类型的混合运算,此处先略过,在下文讲解函数时细说;
struct ArrayUse:
注:此结构体表示数组的赋值表达式
int grammartype:不同语法的种类(目前没有发现有什么特别大的用处);
struct ArrExpr *arrexpr:表示数组的结构体,容易理解;
struct Expression *expression:表示一个赋值的表达式,例如:a[10] = n,那么n就是存储在expression里面的变量;
std::string id:表示变量的名称,一般用于给单变量赋值的,例如:n = 10;
std::string op:表示赋值的运算符号,在此实验中仅有” = “;
struct VarDef:
注:此结构体表示数组的声明表达式
int grammartype:不同语法的种类(目前没有发现有什么特别大的用处);
std::string id:表示声明元素的名称,例如:int n;int a[100];
int dimension:表示数组的维度,单变量是没有该项的
std::vector<struct Expression*> length
和前文所叙述的作作用一样
实现思路
- 该实验的核心思想就是要算出expression里面的值,然后与存储在idmap里面的进行比较,判断是否出现了数组索引越界的情况,故问题就简化成了如何写int op_expression(Expression *expression);
- 而该函数内首要就是要判断expression里面的各种元素是否存在
- 该函数的返回值就应该是数组的索引值
- 这里为了便于处理expression里面存在数组的情况又写了一个int op_array(ArrExpr *arrexpr),
- 该函数的返回值是数组里面存储的值,例如:a[100] = 10, 那么这个函数的任务就是返回10
- 这次实验我认为比较巧妙的地方就是这上面上个函数互相调用处理,互相递归,简化了问题处理
实验的函数分析
int op_expression(Expression* expression)
> > > > 判断里面是否有数字,empty函数用来判断容器是否为空,较于stoi和c_str()更加方便 > > >1
2
3
4
5
- > 1.判断表达式内的元素
>
> > ```c++
> > if (expression->digitsequence.empty())> > > > 判断里面是否存在变量n之类的 > > >1
if (expression->id.empty())
> > > > 判断里面是否存在运算符号 > > >1
if (expression->op.empty())
> > > > 判断里面是否存在数组元素 > > > > > ==注:为了向下判断数组的时候方便,定义一个全局布尔型的变量checkflag== > > > > > 为了便于向下取数组的值的时候更加便利,定义此变量。一旦发现任何一个数组出现了越界行为,便把checkflag的值变成true。同时在op_experssion中判断一下checkflag,如果为true的话直接返回一个负值,然后在checker.cpp里面判断checkflag1
if (expression->arrexpr)
int op_array(ArrExpr *arrexpr)
> > 2.判断该数组是否越界 > > > - ```c++ > > uselen1 = op_expression(arrexpr->length[0]); > > uselen2 = op_expression(arrexpr->length[1]); > > > > if (uselen1 >= idmap[id] || uselen2 >= idmap[id + "1"] || uselen1 < 0 || uselen2 < 0) > >1
2
3
4
5
6
7
8
- > 1.判断数组的维度
>
> > - ```c++
> > if (arrexpr->dimension == 1){
> > }
> > else if (arrexpr->dimension == 2){
> > }> > > > 以二维数组为例1
2
3
4
5
6
7
以二维数组为例,这里op_array函数调用了op_expression函数
3.取出数组的赋值
- ```c++
value = idmap[id + to_string(uselen1) + to_string(uselen2)]```c++
void checker()1
2
3
4
5
6
7
> 1.声明数组
>
> > 声明的时候注意判断该声明是单变量还是数组
> >
> > ```c++
> > if (!def->dimension)表示没有维度,说明是单变量
2.使用数组
每次使用数组的时候记得保存数组的值,以防之后可能会调用该值
1
2value = op_expression(arruse->expression);
idmap[id + to_string(uselen1) + to_string(uselen2)] = value;
实验心得
万事开头难,此言得之。刚开始也是反复看了好几遍实验手册才把各种结构体给弄清楚,各种嵌套啥的。其实这次实验的收获还是蛮大的,除了自己思考了实现代码的逻辑层之外,还发现和学会了几种函数的用法,最重要的让我明白了:某些东西看不懂是由于不熟悉而造成的,实际上的难度可能并没有那么大。总而言之,千万不要轻言放弃,放弃之后再想坚持就难了