C bison/flex生成的最小代码存在内存泄漏

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

在调试一个大型项目的内存泄漏时,我发现泄漏源似乎是一些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 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安装缺少这个接口。