освобождение строки, выделенной в strdup (), от flex/bison

у меня есть flex-код, который копирует строковую лексему с помощью strdup().

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

%}
%option noyywrap

%%

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

%%

strdup() выделяет память и копирует в нее входную строку и возвращает (strdup () - что он делает в C?), поэтому я думаю, что мне нужно освободить его, когда он мне больше не нужен.

из этого поста:когда %destructor вызывается в BISON?, я добавил %destructor { free($$); printf("free");} STRING в файле yacc.

однако я не вижу free() вызывается даже тогда, когда yylval.sval присваивается новая строка, возвращенная из strdup().

что может быть не так? Как освободить выделенную строку в flex / bison?

добавил

Я думаю об использовании статически выделенного sval следующим образом:

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

теперь flex-код становится (без кода проверки, если yytext меньше 100 байт):

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

Я не уверен, что этот подход является тем, что люди обычно используют.

Added2

для моего приложения, простой стажировки в порядке.

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; 
}

1 ответов


как вы говорите, вам нужно освободить строку " когда мне это больше не нужно."Это так просто (или сложно), как это.

C не имеет сборщика мусора, и поэтому программисты C отвечают за знание того, когда выделенная память больше не нужна. Язык не пытается понять это, и (в основном) ни Зубр.

если у вас есть правило сокращения, которое снабжено одним или несколькими семантическими значениями, содержащими указатели на выделенную память, это правило может сделать что угодно. Он может передавать семантические значения в новое семантическое значение, обычно копируя только указатель. Он может скопировать семантическое значение, а затем освободить оригинал. Он может добавить семантическое значение в глобальную структуру данных синтаксического анализа, например таблицу символов.

во всех этих случаях программист должен знать, требуется ли выделенная память, и должен вызывать свободное выделение, если оно не.

однако, есть несколько случаев, в которых Зубр отбрасывает семантическое значение без его когда-либо представленного действия сокращения. Большинство из этих ошибок. Если в рамках восстановления ошибок Зубр решает отказаться от токена, семантическое значение этого токена может привести к утечке памяти. И именно для этого случая у зубра есть %destructor декларации. The %destructor код вызывается, если (и только если) bison отбрасывает токен в результате восстановления ошибок или очистки после ошибок. Все остальные дела-ваша ответственность.

пытаясь избежать этой ответственности, делая слоты стека огромными (например, включая char[100] в семантическом объединении значений) является небезопасным и неэффективным. Это небезопасно, потому что вам нужно постоянно знать, что буфер фиксированного пространства может переполниться, а это означает, что синтаксический анализ программы может перезаписать произвольную память. Это неэффективно, потому что вы в конечном итоге сделать стек на несколько порядков больше, чем необходимо; а также потому, что вы постоянно копируете слоты стека (по крайней мере, дважды для каждый правило сокращения, даже те, которые используют действие по умолчанию.)

вычисление времени жизни семантического значения усложняется только в том случае, если вы собираетесь делиться памятью. Это обычно не полезно для строковых литералов (как в вашем примере), но это может быть очень полезно для имен переменных; большинство имен происходит более одного раза в программе, поэтому всегда есть соблазн использовать одна и та же строка символов для каждого вхождения.

обычно я решаю проблему идентификатора, "интернируя" строку в лексере. Лексер поддерживает таблицу глобальных имен синтаксического анализа - скажем, простое set реализовано с хэш-таблицей -- и для каждого идентификатора, с которым он сталкивается, он добавляет идентификатор в таблицу имен и передает уникальный указатель записи имени в качестве семантического значения. В какой-то момент после окончания синтаксического анализа можно освободить всю таблицу имен, освободив все идентификаторы.

для строковых литералов и других, вероятно, уникальных строк вы можете использовать таблицу имен в любом случае, или вы можете избежать двух копий указателя на одну и ту же символьную строку. Использование таблицы name имеет преимущество в уменьшении объема работы, которую вам нужно сделать в управлении памятью, но за счет возможного сохранения ненужных строк в течение дополнительного времени. Это во многом зависит от характера результата анализа: если это АСТ, то вы, вероятно, нужно сохраняйте символьные строки до тех пор, пока существует AST, но если вы выполняете прямое выполнение или генерацию кода с одним проходом, вам могут не понадобиться строковые литералы в долгосрочной перспективе.