我有Flex / Yacc程序,当它在VC ++ 2012 IDE中运行时会导致断点。断点发生在指令上(在下面的pre_lxr.l中):
free(pre_fname);
项目包含词法分析器(.l文件),yacc文件(.y)和接口文件(位于解析器和程序的C ++部分之间)。问题似乎与内存损坏/泄漏有关。请参阅下面的代码(我已经包含了我认为必要的相关代码)。我也不确定yyterminate电话。
/* pre_prs_ifc.h */
#define MAX_PRE_ERR 120
#define MAX_BANKS 16
#define MAX_PRES 512
typedef struct
{
char *bank;
char *name;
} preStruct;
void initPrePrs();
void appBank(int line_num, const char *name);
void appPre(int line_num, const char *bank, const char *name);
void freePrePrs();
char pre_err[MAX_PRE_ERR];
int num_banks;
int num_pres;
char* pre_fname;
int pre_lnum;
char* bank[MAX_BANKS];
preStruct pre[MAX_PRES];
/* pre_prs_ifc.c */
#include "pre_prs_ifc.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void initPrePrs()
// initialises parser variables
{
pre_err[0] = 0;
num_banks = 0;
num_pres = 0;
pre_lnum = 1;
}
void appBank(int line_num, const char *name)
{
if (num_banks < MAX_BANKS)
bank[num_banks++] = _strdup(name);
else
sprintf(pre_err, "Error on line %d: maximum number of banks
exceeded.", line_num);
}
void appPre(int line_num, const char *bank, const char *name)
{
if (num_pres < MAX_PRES)
{
pre[num_pres].bank = _strdup(bank);
pre[num_pres++].name = _strdup(name);
}
else
sprintf(pre_err, "Error on line %d: maximum number of presets
exceeded.", line_num);
}
void freePrePrs()
// frees parser variables
{
int i;
for (i = 0; i < num_banks; i++)
free(bank[i]);
for (i = 0; i < num_pres; i++)
{
free(pre[i].bank);
free(pre[i].name);
}
}
/* pre_lxr.l */
%{
#include "pre_prs_ifc.h"
#include "pre_prs.h"
#include <stdio.h>
%}
%option outfile="pre_lxr.c" header-file="pre_lxr.h"
%option prefix="pre"
%option warn reentrant noyywrap never-interactive nounistd bison-bridge
%option debug
%%
"banks"|"BANKS" { return banks; }
"presets"|"PRESETS" { return presets; }
"{" { return l_bracket; }
"}" { return r_bracket; }
";" { return semicolon; }
\"([^\\\"\n]|\\.)+\" {
yylval->str = (char*)malloc(strlen(yytext) - 1);
strncpy(yylval->str, &yytext[1], strlen(yytext) - 2);
return quoted_str;
}
[ \t] { }
[\r\n] { pre_lnum++; }
<<EOF>> {
free(pre_fname);
if (!YY_CURRENT_BUFFER)
yyterminate();
}
%%
int preerror(const char *msg)
{
sprintf(pre_err, "Error in %s on line %d.", pre_fname, pre_lnum);
return 0;
}
/* pre_prs.y */
%{
#include "pre_prs_ifc.h"
#include "pre_prs.h"
#include "pre_lxr.h"
int preerror(yyscan_t scanner, const char *msg);
%}
%code requires
{
#ifndef YYSCAN_T
#define YYSCAN_T
typedef void* yyscan_t;
#endif
}
%output "pre_prs.c"
%defines "pre_prs.h"
%name-prefix "pre"
%define api.pure
%lex-param { yyscan_t scanner }
%parse-param { yyscan_t scanner }
%error-verbose
%union
{
char ch;
char* str;
}
%destructor { free($$); } <str>
%token<ch> l_bracket r_bracket semicolon
%token<str> quoted_str
%token<num> banks presets
%%
cmds:
| cmds cmd
;
cmd:
bank_list
|
pre_list
;
bank_list:
banks l_bracket bank_decls r_bracket
;
bank_decls:
| bank_decls bank_decl
;
bank_decl:
quoted_str semicolon
{
fprintf(stderr, "append bank %s\n", $1);
appBank(pre_lnum, $1);
free($1);
}
;
pre_list:
presets l_bracket pre_decls r_bracket
;
pre_decls:
| pre_decls pre_decl
;
pre_decl:
quoted_str quoted_str semicolon
{
fprintf(stderr, "append preset, bank %s, name %s\n", $1, $2);
appPre(pre_lnum, $1, $2);
free($1);
free($2);
}
;
%%
/* C++ using the above code */
extern "C"
{
#include "pre_prs_ifc.h"
#include "pre_prs.h"
#include "pre_lxr.h"
}
int yyparse(yyscan_t scanner);
...
bool CMainDlg::loadBankPre(std::string fname)
// load bank and preset lists
{
int err, i;
yyscan_t scanner;
FILE *src;
std::string name_str;
CListBox* bank_lb;
bool ret_val;
pre_fname = _strdup(fname.c_str());
if (prelex_init(&scanner))
{
m_err = "Error initialising scanner.";
return false;
}
src = fopen(fname.c_str(), "r");
if (src == NULL)
{
m_err = "Could not open file: " + fname;
ret_val = false;
}
else
{
preset_in(src, scanner);
initPrePrs();
err = preparse(scanner);
if (err)
{
m_err = pre_err;
ret_val = false;
}
else
{
if (pre_err[0] == 0)
{
bank_lb = (CListBox*)GetDlgItem(IDC_LI_BANK);
for (i = 0; i < num_banks; i++)
bank_lb->AddString(bank[i]);
ret_val = true;
}
else
{
m_err = pre_err;
ret_val = false;
}
}
fclose(src);
}
pre_delete_buffer(0, scanner);
prelex_destroy(scanner);
return ret_val;
}
CMainDlg::~CMainDlg()
// destructor
{
freePrePrs();
}
答案 0 :(得分:1)
您应该永远在头文件中定义全局变量。头文件必须声明它们为 extern ,并且它们需要在一个翻译单元中定义。
如上所述,每个翻译单元都有自己的全局变量定义,包括pre_fname
。这是未定义的行为,并且无法保证不同翻译单元中使用的名称指的是相同的存储位置。甚至比修复声明更好的方法是将它们传递给解析器和扫描器,以避免使用全局变量。
无论如何,在CMainDlg :: loadBankPre中创建了pre_fname
(strdup
},并且在同一个函数中也free
也是最有意义的,理想情况下使用智能指针。在你这样做的情况下,你根本不需要<<EOF>>
规则。
此外,strncpy
在这里确实不是一个好主意。正如@PaulOgilvie在评论中指出的那样,它使复制的字符串无法终止,结果是strdup
中的后appBank
将复制未确定数量的额外字节,可能引用无效内存。 (顺便说一句,您不需要在strlen
上致电yytext
.Flex会为此提供变量yyleng
,从而节省对令牌的额外扫描。)
另一方面,当您格式化为固定长度的缓冲区(sprintf(pre_err...)
)时,应使用snprintf
。否则,您可能会超出pre_err
,最终可能会覆盖pre_fname
。
此外,preerror
的原型与其定义不一致。因此,如果它被调用,会发生一些不好的事情。
最后,你的<<EOF>>
规则(这是不必要的,参见上文)不会返回0,因此词法分析器将继续尝试扫描。 <<EOF>>
规则必须返回0或提供新的输入缓冲区。 (或以其他方式退出。)在这种情况下,由于<<EOF>>
规则调用free
,规则的第二次(及后续)激活将多次释放相同的存储空间。
答案 1 :(得分:0)
我发现EOF部分被反复调用,导致无限循环,可以通过调用yypop_buffer_state来停止。
<<EOF>> {
yypop_buffer_state(yyscanner);
free(pre_fname);
if (!YY_CURRENT_BUFFER)
yyterminate();
}
所以免费是第二次围绕EOF部分导致崩溃。正如rici指出的那样(在这种情况下)如果free被移动到函数loadBankPre,则根本不需要EOF部分。