【问题标题】:Implementing finite-state machine: run-time error, why?实现有限状态机:运行时错误,为什么?
【发布时间】:2020-03-19 09:37:30
【问题描述】:

我知道有可用的 FSA 实现,但我很想知道我在学习方面的错误在哪里。运行时错误通常发生在使用>> 从输入文件中读取时。

错误是“抛出异常:exercise.exe 已触发 断点”。

我问过我的同学,但他们也无法确定错误。

输入:

  • 第一行:状态数

  • 第二行:开始状态的#

  • 第三行:接受状态数

  • 接下来的几行:接受状态的#s

  • 下一行:转换函数的数量

  • 下几行:转换函数,形式为from to value,其中from和to是对应的状态, em>值是一个符号

  • 下一行:我们需要检查是否接受的字符串数

  • 接下来的几行:字符串本身

我的代码是:

#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <string>
using namespace std;

ifstream I("input.txt");
ofstream O("output.txt");

struct arrow {
    int destination=-1;
    char c='x';
    arrow() {}
    arrow(char val, int to) {
        destination = to;
        c = val;
    }
};
struct node {
    int name=-1;
    bool accept=false;
    int ars=0;
    arrow* arrows=new arrow;
    node() {}
    node(bool isAccept, int whatName) {
        accept = isAccept;
        name = whatName;
    }
};
class fsa {
    int start;
    node* states=new node;
public:
    fsa(int n, int entrSt, int acceptSts) {
        this->start=entrSt;
        int* tempForAcceptStates = new int[acceptSts];
        for (int i = 0; i < acceptSts; i++) I >> tempForAcceptStates[i];
        for (int i = 0; i < n; i++) { 
            int wasNodeCreated = -1;
            for (int j = 0; j < acceptSts; j++) {
                if (tempForAcceptStates[j] == i) wasNodeCreated = i;
            }
            if (wasNodeCreated >= 0) states[i] = node(true, wasNodeCreated);
            else { states[i] = node(false, i); }
        }
        int transitions;
        I >> transitions;
        for (int i = 0; i < transitions; i++) {
            int from, to;
            char val;
            I >> from >> to >> val;
            int temp = states[from].ars;
            this->states[from].arrows[temp] = arrow(val, to);
            states[from].ars++;
        }
    }
     bool isRecognized() {
         string str;
         getline(I, str);
         bool wasSymbolFound=false;
         int currentNode = this->start;
         for (int i = 0; i < str.length(); i++) {
             int howManyArsCurrentNodeHas = states[currentNode].ars;
             for (int j = 0; j < howManyArsCurrentNodeHas; j++) {
                wasSymbolFound = false;
                 if (states[currentNode].arrows[j].c == str[i]) {
                     currentNode = states[currentNode].arrows[j].destination;
                     wasSymbolFound = true;
                }
                 if (wasSymbolFound==true) break;
             }
             if (wasSymbolFound == false) return false;
         }
         if (states[currentNode].accept==true) return true; else return false;
     }
};

void main() {
    int n, entrSt, acceptSts;
    I >> n >> entrSt >> acceptSts;
    fsa A(n, entrSt, acceptSts);
    int words;
    I >> words;
    for (int i = 0; i < words; i++) {
        if (A.isRecognized()) O << "YES\n"; else O << "NO\n";
    }
    }

示例输入:

3
0
1
0
6
0 1 a
1 0 b
0 2 b
1 2 a
2 2 a
2 2 b
3
ab
aaa
abababab

对应的期望输出:

YES
NO 
YES

【问题讨论】:

  • 错误是什么?
  • @RomeoValentin 错误是“抛出异常:executive.exe 已触发断点”。

标签: c++ state-machine finite-automata


【解决方案1】:

请注意,您的文件可能找不到,或者文件中的某些格式错误可能会导致读取错误。

不幸的是,您没有进行错误检查,这会使您面临 UB 的风险,例如,您认为从文件中正确读取的变量中有随机值。例如,在我的系统上,在没有 input.txt 文件的情况下尝试代码会导致发生内存分配异常,因为 acceptSts 是一个巨大的随机数。

现在更详细地了解正在发生的事情,即使使用正确一致的输入文件作为示例,您的代码也注定会失败。

在fsa 构造函数中,您使用new node 初始化states 成员,因此是指向单个节点的指针。稍后您分配或读取索引值,例如states[i],states[from],states[temp]。但是你从来没有分配一个节点数组。因此,对于每个非零索引,您都会得到 UB,这可能会导致内存损坏、程序突然中止、任何其他奇怪的行为,或者根本没有任何症状。

您可以通过初始化构造函数中的状态来纠正这个问题,因为有限状态是已知的:

    states = new node[n];

不幸的是,arrow 也有同样的问题:

 states[from].arrows[temp] = arrow(val, to); 

不仅node 中的arrows 使用指向单个箭头的指针进行了初始化,导致与前面解释的问题相同。使用原始分配来解决这个问题更加复杂。所以我的建议是,不要使用new 分配数组,如果允许,请尝试使用vector。向量可以动态增长并且不易出错。

不相关:

  • 使用全局I 和O 并在类方法中使用它会产生隐藏的耦合,从而使程序难以调试。更好的方法是将这些fstream 对象定义为本地对象(例如main()),并将它们作为istream 和ostream 引用传递给需要它们的函数。
  • 如果你在构造函数中分配了一些指针,你也应该在析构函数中删除它,否则你可能会泄漏内存。如果你分配了new[],那么delete[]。

【讨论】:

  • 非常非常感谢。我添加了states = new node[n];,并将箭头放入向量中。然后代码通过了所有测试。谢谢你让我上大学。
【解决方案2】:

请添加错误信息,如果我们不知道是什么问题,帮助您很痛苦。

但我认为,您的问题在于fsa::states 的定义。您将其定义为new node;,它是指向堆上node 的指针,但您想在堆上创建nnodes 的数组。稍后写入n[i] 会尝试访问您尚未分配的一些内存,因此您会遇到访问冲突。

不要在 C++ 中使用 C 风格的动态数组 (new []),而是使用 std::vector。

【讨论】:

  • 错误是“抛出异常:executive.exe 已触发断点”。 (也谢谢!我从未听说过std::vector,我正在查找它。)
猜你喜欢
  • 2012-06-26
  • 1970-01-01
  • 2020-11-18
  • 1970-01-01
  • 1970-01-01
  • 2015-10-14
  • 1970-01-01
  • 2011-03-31
  • 1970-01-01
相关资源
最近更新 更多