pro*_*eek 6 c yacc bison strdup flex-lexer
我有使用复制字符串lexeme的flex代码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];} ;
%%
Run Code Online (Sandbox Code Playgroud)
strdup()分配内存并将输入字符串复制到其中并返回(strdup() - 它在C中做了什么?),所以我想当我不再需要它时我需要释放它.
从这篇文章:什么是在BISON中调用%析构函数?,我%destructor { free($$); printf("free");} STRING在yacc文件中添加了.
但是,free()即使yylval.sval从返回的新字符串赋值,我也看不到调用strdup().
可能有什么问题?如何释放flex/bison中分配的字符串?
我想如下使用静态分配的sval:
%union {
int ival;
char sval[100]; // char* sval;
}
Run Code Online (Sandbox Code Playgroud)
flex代码现在变为(如果yytext小于100字节,则没有检查代码):
\"[a-zA-Z]+\" {
//yylval.sval = strdup(yytext);
memset(yylval.sval, 0, 100);
strcpy(yylval.sval, yytext);
return STRING;
}
Run Code Online (Sandbox Code Playgroud)
我不确定这种方法是否是人们通常使用的方法.
对于我的应用程序,简单的实习是可以的.
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];
Run Code Online (Sandbox Code Playgroud)
对于yacc代码
%union {
int ival;
char *sval;
}
Run Code Online (Sandbox Code Playgroud)
正如你所说,你需要释放字符串"当我不再需要它时".它就像那样简单(或复杂).
C没有垃圾收集器,因此C程序员负责知道何时不再需要分配的内存.该语言并不试图解决这个问题,而且(大多数情况下)也没有野牛.
如果您有一个缩减规则,该规则具有一个或多个包含指向已分配内存的指针的语义值,则该规则可能会执行许多操作.它可能会将语义值传递给新的语义值,通常只通过复制指针.它可以复制语义值,然后释放原始文件.它可能会将语义值添加到解析全局数据结构中,如符号表.
在所有这些情况下,程序员应该知道是否仍然需要分配的内存,如果不是,则应该调用free.
但是,在少数情况下,野牛会丢弃语义价值而不会将其呈现给减少行动.其中大多数是错误条件.如果作为错误恢复的一部分,bison决定丢弃一个令牌,那么该令牌的语义值可能会泄漏内存.正是在这种情况下,野牛才有了%destructor宣言.%destructor如果(并且仅当)bison因错误恢复或错误后清理而丢弃令牌,则调用该代码.所有其他情况都是您的责任.
试图通过使堆栈槽变得庞大(例如char[100]在语义值联合中包含一个)来逃避这种责任既不安全又效率低下.这是不安全的,因为您需要经常意识到固定空间缓冲区可能会溢出,这意味着解析语法上有效的程序可能会覆盖任意内存.这是低效的,因为你最终使堆栈比必要的大几个数量级; 并且还因为你不断复制堆栈插槽(每个缩减规则至少两次,甚至是使用默认操作的规则.)
如果您打算共享内存,则只能弄清楚语义值的生命周期.这通常对字符串文字没有用(如在你的例子中),但它对变量名称非常有用; 大多数名称在程序中出现不止一次,因此总是有诱惑在每次出现时使用相同的字符串.
我通常通过"实习"词法分析器中的字符串来解决标识符问题.词法分析器维护一个解析全局名称表 - 比如set用哈希表实现的简单表 - 并且对于它遇到的每个标识符,它将标识符添加到名称表并将唯一名称条目指针作为语义值传递.在解析结束后的某个时刻,可以释放整个名称表,释放所有标识符.
对于字符串文字和其他可能唯一的字符串,您既可以使用名称表,也可以避免使用指向同一字符串的指针的两个副本.使用名称表的优点是可以减少在内存管理中需要完成的工作量,但代价是可能会在不必要的时间内保留不必要的字符串.这很大程度上取决于解析结果的性质:如果它是AST,那么只要AST存在就可能需要保留字符串,但是如果你正在进行直接执行或一次性代码生成,那么你可能会从长远来看,不需要字符串文字.
| 归档时间: |
|
| 查看次数: |
2458 次 |
| 最近记录: |