7

我有使用strdup().

%{   
#include "json.tab.h"
#define YY_DECL extern "C" int yylex()

%}
%option noyywrap

%%

[ \t\n]+ ; 
\"[a-zA-Z]+\" {yylval.sval = strdup(yytext); return STRING; }
[0-9]+ {yylval.ival = atoi(yytext); return NUMBER; }
. {return yytext[0];} ; 

%%

strdup()分配内存并将输入字符串复制到其中并返回(strdup() - 它在 C 中做了什么?),所以我想当我不再需要它时需要释放它。

来自这篇文章:什么时候在 BISON 中调用 %destructor?,我%destructor { free($$); printf("free");} STRING在yacc文件中添加了。

但是,free()即使yylval.sval分配了从strdup().

可能有什么问题?如何释放 flex/bison 中分配的字符串?

添加

我考虑使用静态分配的 sval 如下:

%union {
    int ival;
    char sval[100]; // char* sval;
}

弹性代码现在变为(如果 yytext 小于 100 字节,则没有检查代码):

\"[a-zA-Z]+\" {
    //yylval.sval = strdup(yytext);
    memset(yylval.sval, 0, 100);
    strcpy(yylval.sval, yytext);
    return STRING; 
}

我不确定这种方法是否是人们通常使用的方法。

添加2

对于我的申请,简单的实习是可以的。

extern char buffer[]; // [100];
%}
%option noyywrap

%%

\"[a-zA-Z]+\" {
        //yylval.sval = strdup(yytext);
        memset(buffer, 0, 100);
        strcpy(buffer, yytext);
        yylval.sval = buffer;
        return STRING; 
    }
...

char buffer[100];

对于 yacc 代码

%union {
    int ival;
    char *sval; 
}
4

1 回答 1

9

正如您所说,您需要释放字符串“当我不再需要它时”。就这么简单(或复杂)。

C 没有垃圾收集器,因此 C 程序员有责任知道何时不再需要分配的内存。该语言并没有试图弄清楚它,而且(大多数情况下)野牛也没有。

如果您有一个缩减规则,它提供了一个或多个包含指向已分配内存的指针的语义值,那么该规则可能会做许多事情中的任何事情。它可能会将语义值传递给新的语义值,通常只复制指针。它可能会复制语义值,然后释放原始值。它可能会将语义值添加到解析全局数据结构中,例如符号表。

在所有这些情况下,程序员都应该知道分配的内存是否仍然需要,如果不需要,应该调用 free 分配的内存。

但是,在少数情况下,bison 会丢弃语义值,而不会将其呈现给归约操作。其中大部分是错误条件。如果作为错误恢复的一部分,bison 决定丢弃一个标记,则该标记的语义值可能会泄漏内存。而正是针对这种情况,bison 有了%destructor声明。%destructor当(且仅当)bison 由于错误恢复或错误后清理而丢弃令牌时调用该代码。所有其他情况由您负责。

试图通过使堆栈槽变得巨大(例如char[100]在语义值联合中包含 a )来逃避这种责任既不安全又低效。这是不安全的,因为您需要经常注意固定空间缓冲区可能会溢出,这意味着解析语法上有效的程序可能会覆盖任意内存。这是低效的,因为您最终使堆栈比必要的大几个数量级;并且还因为您最终会不断复制堆栈槽(每个缩减规则至少两次,即使是使用默认操作的那些。)

仅当您打算共享内存时,才能确定语义值的生命周期很复杂。这通常对字符串文字没有用(如您的示例中),但它对变量名很有帮助;大多数名称在一个程序中出现不止一次,因此总是很容易对每次出现使用相同的字符串。

我通常通过在词法分析器中“实习”字符串来解决标识符问题。词法分析器维护一个解析全局名称表——比如说,一个set用哈希表实现的简单名称——并且对于它遇到的每个标识符,它都将标识符添加到名称表中,并将唯一的名称条目指针作为语义值传递。在解析结束后的某个时刻,可以释放整个名称表,释放所有标识符。

对于字符串文字和其他可能唯一的字符串,您可以使用名称表,也可以避免拥有指向同一字符串的指针的两个副本。使用名称表的优点是可以减少您在内存管理中需要做的工作量,但代价是可能会保留不必要的字符串以延长时间。这在很大程度上取决于解析结果的性质:如果它是 AST,那么只要 AST 存在,您可能需要保留字符串,但如果您正在执行直接执行或一次性代码生成,您可能从长远来看,不需要字符串文字。

于 2015-06-28T22:28:51.413 回答