前言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函数不能用在一个空的字符串上,否则会报错

    stoi.png

    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)
    
    1
    2
    3
    4
    5

    - > 1.判断表达式内的元素
    >
    > > ```c++
    > > if (expression->digitsequence.empty())
    > > > > 判断里面是否有数字,empty函数用来判断容器是否为空,较于stoi和c_str()更加方便 > > >
    1
    if (expression->id.empty())
    > > > > 判断里面是否存在变量n之类的 > > >
    1
    if (expression->op.empty())
    > > > > 判断里面是否存在运算符号 > > >
    1
    if (expression->arrexpr)
    > > > > 判断里面是否存在数组元素 > > > > > ==注:为了向下判断数组的时候方便,定义一个全局布尔型的变量checkflag== > > > > > 为了便于向下取数组的值的时候更加便利,定义此变量。一旦发现任何一个数组出现了越界行为,便把checkflag的值变成true。同时在op_experssion中判断一下checkflag,如果为true的话直接返回一个负值,然后在checker.cpp里面判断checkflag
  • int op_array(ArrExpr *arrexpr)
    
    1
    2
    3
    4
    5
    6
    7
    8

    - > 1.判断数组的维度
    >
    > > - ```c++
    > > if (arrexpr->dimension == 1){
    > > }
    > > else if (arrexpr->dimension == 2){
    > > }
    > > 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

    以二维数组为例,这里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
    2
    value = op_expression(arruse->expression);
    idmap[id + to_string(uselen1) + to_string(uselen2)] = value;

实验心得

万事开头难,此言得之。刚开始也是反复看了好几遍实验手册才把各种结构体给弄清楚,各种嵌套啥的。其实这次实验的收获还是蛮大的,除了自己思考了实现代码的逻辑层之外,还发现和学会了几种函数的用法,最重要的让我明白了:某些东西看不懂是由于不熟悉而造成的,实际上的难度可能并没有那么大。总而言之,千万不要轻言放弃,放弃之后再想坚持就难了


感谢阅读