C bison/flex生成的最小代码存在内存泄漏
在调试一个大型项目的内存泄漏时,我发现泄漏源似乎是一些flex/bison生成的代码。我能够用以下由两个文件组成的最小示例重新创建泄漏,C bison/flex生成的最小代码存在内存泄漏,c,memory-leaks,bison,flex-lexer,C,Memory Leaks,Bison,Flex Lexer,在调试一个大型项目的内存泄漏时,我发现泄漏源似乎是一些flex/bison生成的代码。我能够用以下由两个文件组成的最小示例重新创建泄漏,sand.l和sand.y: %{ #include <stdio.h> #include <stdlib.h> int yylex(); int yyparse(); FILE* yyin; void yyerror(const char* s); %} %token INT %% program: program
sand.l
和sand.y
:
%{
#include <stdio.h>
#include <stdlib.h>
int yylex();
int yyparse();
FILE* yyin;
void yyerror(const char* s);
%}
%token INT
%%
program:
program INT { puts("Found integer"); }
|
;
%%
int main(int argc, char* argv[]) {
yyin = stdin;
do {
yyparse();
} while (!feof(yyin));
return 0;
}
void yyerror(const char* s) {
puts(s);
}
在sand.l
中:
%{
#include <stdlib.h>
#include "sand.tab.h"
%}
%%
[0-9]+ { return INT; }
. ;
%%
代码是用
$ bison -d sand.y
$ flex sand.l
$ gcc -g lex.yy.c sand.tab.c -o main -lfl
使用valgrind运行程序时出现以下错误:
8 bytes in 1 blocks are still reachable in loss record 1 of 3
at 0x4C2AC3D: malloc (vg_replace_malloc.c:299)
by 0x40260F: yyalloc (lex.yy.c:1723)
by 0x402126: yyensure_buffer_stack (lex.yy.c:1423)
by 0x400B89: yylex (lex.yy.c:669)
by 0x402975: yyparse (sand.tab.c:1114)
by 0x402EC4: main (sand.y:24)
64 bytes in 1 blocks are still reachable in loss record 2 of 3
at 0x4C2AC3D: malloc (vg_replace_malloc.c:299)
by 0x40260F: yyalloc (lex.yy.c:1723)
by 0x401CBF: yy_create_buffer (lex.yy.c:1258)
by 0x400BB3: yylex (lex.yy.c:671)
by 0x402975: yyparse (sand.tab.c:1114)
by 0x402EC4: main (sand.y:24)
16,386 bytes in 1 blocks are still reachable in loss record 3 of 3
at 0x4C2AC3D: malloc (vg_replace_malloc.c:299)
by 0x40260F: yyalloc (lex.yy.c:1723)
by 0x401CF6: yy_create_buffer (lex.yy.c:1267)
by 0x400BB3: yylex (lex.yy.c:671)
by 0x402975: yyparse (sand.tab.c:1114)
by 0x402EC4: main (sand.y:24)
bison和/或flex似乎拥有大量内存。还有什么办法强迫他们释放它吗?默认的flex框架分配了一个输入缓冲区和一个小的缓冲区堆栈,它永远不会释放。您可以使用手动释放输入缓冲区,但没有文档记录的方法来删除缓冲区堆栈。如果您有一个足够古老的flex版本[参见注1],那么可以调用
yylex_destroy()
来删除缓冲区堆栈的最后一个遗迹。(如果不这样做,应用程序中只有8个字节,所以这不是一场灾难。)
如果您想编写一个干净的应用程序,那么应该生成一个新的应用程序,它将所有持久数据放入一个scanner上下文对象中。您的代码必须分配和释放此对象,释放它将释放所有内存分配。(您可能还希望生成一个纯解析器,其工作方式大致相同。)
但是,可重入扫描程序有一个非常不同的API,因此需要让解析器通过扫描程序上下文对象。如果还使用可重入(纯)解析器,则需要修改扫描仪操作,因为使用可重入解析器,yylval
是YYSTYPE*
而不是YYSTYPE
笔记:
yylex_destroy()。由于该版本是在近二十年前发布的,您可能会认为此说明是不必要的,但不幸的是,v2.5.4仍然是默认的MinGW安装,并且它在各种Linux发行版上的使用寿命也出奇地长(尽管我认为现在您不太可能发现它已安装)
一个可能相关的问题是8个字节可以被释放。必须调用
yylex_destroy()代码>而不是yy_delete_buffer(yy_CURRENT_buffer)代码>@AleksanderBobiński:是的,你是对的,我更新了答案。当我写它的时候,仍然有很多v2.5.4安装缺少这个接口。