在Bison/Flex中释放内存的位置

Where to free memory in Bison/Flex?

本文关键字:内存 位置 释放 Bison Flex      更新时间:2023-10-16

我用的是Bison &Flex 1个月或多或少,所以我很抱歉,如果我没有看到一些明显的东西(但我不认为是)。

我有一个关于Flex Bison释放内存的问题。下面是我的代码:

 parser.l
 {DATE}      { yylval.str= strdup(yytext);
             pair<string,string> newpair = make_pair("DATE",yytext);
             myvector.push_back(newpair);
              return TOKEN_DATE ;}

这是我的。l文件的一个例子。我将yytext的值复制到yylval.str中。然后用该内容创建一个新对(实际上是键/值),然后返回bison的令牌日期。我的解析器。y不超过yyparse;当某些内容被捕获时,它只是打印它。

我试着在这个上运行valgrind,我有多个关于strdup的错误。我知道这个函数使用malloc,但我不知道在哪里和什么时候使用FREE。

我可能猜它在。y文件中,但是在哪里?

 test:
      TOKEN_DATE                 { cout << $1 << endl; // here ? and what to free ?}

我不太明白这些,我希望能有一个简单明了的解释。

提前感谢,


编辑:

我尝试了以下几种方法:

 test:
      TOKEN_DATE TOKEN_TOTO TOKEN_BLABLA { cout << $1 << endl; free($1); free($2);}
    | TOKEN_DATE test { cout << $1 << endl, free($1); }

它似乎编译和执行良好,但valgrind仍然告诉我,strdup函数中包含的malloc有问题。但是我不能在flex文件中写free(yylval.str),否则,bison将不知道这个值(如果我理解正确,我试过了,它不起作用)。我真的不知道如何消除这个泄漏问题。

当您不再需要复制的字符串时,您需要释放它。在比较简单的情况下,您可以在打印出来之后释放($1),但通常情况下,解析器将复制的字符串插入到某些数据结构中,在这种情况下,该数据结构成为malloc存储的所有者,并且对free的调用将在析构函数中执行。

这与其他资源管理问题没有什么不同;您需要始终清楚谁是已分配资源的所有者,因为所有者有责任在不再需要资源时释放资源。

内部发生的事情是bison维护一个语义值堆栈,每个语义值都具有类型YYSTYPE(即。"语义型"),这也是yylval的类型。当一个令牌被移到堆栈上时,bisonyylval复制到堆栈的顶部。在执行产品对应的动作之前,bison将产品中每个终端和非终端的语义值称为$1, $2等。各种$x符号被替换为对bison堆栈位置的引用。

非终结符也有语义值,因为每个操作将一个值存储到伪变量$$中。(如果动作不这样做,则$$的值是不可预测的,但它仍然存在。)动作完成后,bison移除$1, $2…然后将伪变量$$复制到堆栈的顶部。它不会对弹出的值做任何事情,所以如果它们需要被释放或以其他方式销毁,操作必须自己完成。

因为语义值是简单复制的,所以语义类型不应该包含任何不能简单复制的c++对象。

如果使用%union声明,则语义类型YYSTYPEunion对象,并且需要告诉bison哪个联合标记适用于每个终端和非终端。在这种情况下,$$和所有$n都自动添加了正确的.tag,并且操作在某种程度上变得更加类型安全。

从flex手册:

21.3关于yytext And Memory的说明

当flex找到匹配时,yytext指向在输入缓冲区中匹配。字符串本身是输入的一部分缓冲区,而不是单独分配。yytext的值将为下次调用yylex()时覆盖。简而言之,价值Yytext只在匹配规则的操作中有效。

因此make_pair不应该是如下的吗?

pair<string,string> newpair = make_pair("DATE",yylval.str);

如果是,那么在清理pair内存时应该释放该字符串。

简单的回答是:看情况。当你用完内存后,你应该释放它,无论何时。

更有用的答案:尝试使用尽可能少的内存分配。如果你从来没有malloc任何内存,你从来没有free任何内存。在你给出的例子中,你对一个日期进行模式匹配。通常,日期有一定的格式和长度上限;例如,格式'yyyy/mm/dd'包含10个字符。如果您能够预料到这一点,那么您可以简单地将一个静态大小的字符数组放入yylval,例如char date[10];,而不是创建一个新的字符串来保存日期。