使用 flex、yy_scan_string() 和 input() 直到输入结束的段错误
Seg fault using flex, yy_scan_string(), and input() until end of input
以下 flex 代码似乎创建了一个可执行文件,当输入字符串是未终止的注释时会出现段错误。注意:
- 只有当输入缓冲区是字符串(例如使用 yy_scan_string())而不是文件 (yyset_in())
时才会发生这种情况
- 当 flex 代码试图在 NULL 文件指针上调用 fread() 时发生段错误——即使从未使用过文件缓冲区
- "else return 0" 防止错误发生,但我不清楚这在多个缓冲区的上下文中是否是一个安全的解决方法
- 以下代码仅用于说明目的,请原谅它的不那么真实。
- 我尝试使用 unput() 而不是 "return 0",但似乎没有任何区别
这是一个 flex 错误(否则它为什么会调用 fread())?无论如何,"return 0" 是解决此问题的正确方法,还是会导致其他问题,例如对于嵌套/多个缓冲区?
bug.l:
// to compile: flex bug.l; gcc -g lex.yy.c
// to run: ./a.out "/* unterminated comment"
%option reentrant stack noyywrap
%%
"/*" {
size_t len;
int c1 = 0, c2 = input(yyscanner);
for(len = 1;;len++) {
if(c2 == EOF || !c2) break;
if(c1 == '*' && c2 == '/') break;
c1 = c2;
c2 = input(yyscanner);
}
if(c1 == '*' && c2 == '/')
printf("found end of comment\n");
// else return 0; // without this line, string input of "/*" will cause a seg fault
}
.|\n
<<EOF>> { return 0; }
%%
int main ( int argc, char * argv[] ) {
if(argc < 3 || (strcmp(argv[1], "-s") && strcmp(argv[1], "-f")))
printf("Usage:\n %s -s string_to_scan\n %s -f file_to_scan\n", argv[0], argv[0]);
else {
yyscan_t scanner;
YY_BUFFER_STATE buf;
if(!strcmp(argv[1], "-s")) {
yylex_init ( &scanner );
buf = yy_scan_string(argv[2], scanner);
yylex ( scanner );
yylex_destroy ( scanner );
} else {
FILE *f = fopen(argv[2], "rb");
if(f) {
yylex_init ( &scanner );
yyset_in (f, scanner);
yylex ( scanner );
yylex_destroy ( scanner );
}
}
}
return 0;
}
如果我对您的代码的理解正确,我不能同意您关于 flex 存在错误的看法。您正在读取规则 "/*"
的操作代码中的输入流。这样做违背了词法分析的思想。您提供的代码可能会阻止扫描器设置文件结束标志,并且在为 "/*"
调用操作后,扫描器正在尝试读取已经结束的流。要处理评论,您应该使用弹性状态。
编辑:
您的代码应该如下所示:
%option reentrant stack noyywrap
%s COMMENT
%%
"/*" { BEGIN COMMENT; }
<COMMENT>"*/" { BEGIN 0; }
<COMMENT>. { /*code for ignored comment characters*/ }
.|\n { /*code to handle non-comment characters*/ }
%%
int main ( int argc, char * argv[] ) {
if(argc < 3 || (strcmp(argv[1], "-s") && strcmp(argv[1], "-f")))
printf("Usage:\n %s -s string_to_scan\n %s -f file_to_scan\n", argv[0], argv[0]);
else {
yyscan_t scanner;
YY_BUFFER_STATE buf;
if(!strcmp(argv[1], "-s")) {
yylex_init ( &scanner );
buf = yy_scan_string(argv[2], scanner);
yylex ( scanner );
yylex_destroy ( scanner );
} else {
FILE *f = fopen(argv[2], "rb");
if(f) {
yylex_init ( &scanner );
yyset_in (f, scanner);
yylex ( scanner );
yylex_destroy ( scanner );
}
}
}
return 0;
}
正如所写,如果注释没有终止,您将最终尝试读取 EOF 两次。也就是说,你读了一次(使用 input()
),然后,因为你没有从词法动作中 return,所以你请求 flex 继续扫描需要它读取输入字符的输入.
这是未定义的行为。识别 EOF 后,您必须从扫描器中 return 0。
您的 <<EOF>>
规则是不必要的;如果没有规则,扫描器将通过 returning 0 自动响应 EOF。通常插入 EOF 规则的唯一原因是进行某种清理或切换输入缓冲区。如果您确实有一个重要的 EOF 规则,它必须 return 0 或安排不重试当前输入缓冲区。
以下 flex 代码似乎创建了一个可执行文件,当输入字符串是未终止的注释时会出现段错误。注意:
- 只有当输入缓冲区是字符串(例如使用 yy_scan_string())而不是文件 (yyset_in()) 时才会发生这种情况
- 当 flex 代码试图在 NULL 文件指针上调用 fread() 时发生段错误——即使从未使用过文件缓冲区
- "else return 0" 防止错误发生,但我不清楚这在多个缓冲区的上下文中是否是一个安全的解决方法
- 以下代码仅用于说明目的,请原谅它的不那么真实。
- 我尝试使用 unput() 而不是 "return 0",但似乎没有任何区别
这是一个 flex 错误(否则它为什么会调用 fread())?无论如何,"return 0" 是解决此问题的正确方法,还是会导致其他问题,例如对于嵌套/多个缓冲区?
bug.l:
// to compile: flex bug.l; gcc -g lex.yy.c
// to run: ./a.out "/* unterminated comment"
%option reentrant stack noyywrap
%%
"/*" {
size_t len;
int c1 = 0, c2 = input(yyscanner);
for(len = 1;;len++) {
if(c2 == EOF || !c2) break;
if(c1 == '*' && c2 == '/') break;
c1 = c2;
c2 = input(yyscanner);
}
if(c1 == '*' && c2 == '/')
printf("found end of comment\n");
// else return 0; // without this line, string input of "/*" will cause a seg fault
}
.|\n
<<EOF>> { return 0; }
%%
int main ( int argc, char * argv[] ) {
if(argc < 3 || (strcmp(argv[1], "-s") && strcmp(argv[1], "-f")))
printf("Usage:\n %s -s string_to_scan\n %s -f file_to_scan\n", argv[0], argv[0]);
else {
yyscan_t scanner;
YY_BUFFER_STATE buf;
if(!strcmp(argv[1], "-s")) {
yylex_init ( &scanner );
buf = yy_scan_string(argv[2], scanner);
yylex ( scanner );
yylex_destroy ( scanner );
} else {
FILE *f = fopen(argv[2], "rb");
if(f) {
yylex_init ( &scanner );
yyset_in (f, scanner);
yylex ( scanner );
yylex_destroy ( scanner );
}
}
}
return 0;
}
如果我对您的代码的理解正确,我不能同意您关于 flex 存在错误的看法。您正在读取规则 "/*"
的操作代码中的输入流。这样做违背了词法分析的思想。您提供的代码可能会阻止扫描器设置文件结束标志,并且在为 "/*"
调用操作后,扫描器正在尝试读取已经结束的流。要处理评论,您应该使用弹性状态。
编辑:
您的代码应该如下所示:
%option reentrant stack noyywrap
%s COMMENT
%%
"/*" { BEGIN COMMENT; }
<COMMENT>"*/" { BEGIN 0; }
<COMMENT>. { /*code for ignored comment characters*/ }
.|\n { /*code to handle non-comment characters*/ }
%%
int main ( int argc, char * argv[] ) {
if(argc < 3 || (strcmp(argv[1], "-s") && strcmp(argv[1], "-f")))
printf("Usage:\n %s -s string_to_scan\n %s -f file_to_scan\n", argv[0], argv[0]);
else {
yyscan_t scanner;
YY_BUFFER_STATE buf;
if(!strcmp(argv[1], "-s")) {
yylex_init ( &scanner );
buf = yy_scan_string(argv[2], scanner);
yylex ( scanner );
yylex_destroy ( scanner );
} else {
FILE *f = fopen(argv[2], "rb");
if(f) {
yylex_init ( &scanner );
yyset_in (f, scanner);
yylex ( scanner );
yylex_destroy ( scanner );
}
}
}
return 0;
}
正如所写,如果注释没有终止,您将最终尝试读取 EOF 两次。也就是说,你读了一次(使用 input()
),然后,因为你没有从词法动作中 return,所以你请求 flex 继续扫描需要它读取输入字符的输入.
这是未定义的行为。识别 EOF 后,您必须从扫描器中 return 0。
您的 <<EOF>>
规则是不必要的;如果没有规则,扫描器将通过 returning 0 自动响应 EOF。通常插入 EOF 规则的唯一原因是进行某种清理或切换输入缓冲区。如果您确实有一个重要的 EOF 规则,它必须 return 0 或安排不重试当前输入缓冲区。