C 为什么这个bison代码会产生意外的输出?

C 为什么这个bison代码会产生意外的输出?,c,bison,flex-lexer,C,Bison,Flex Lexer,弹性代码: 1 %option noyywrap nodefault yylineno case-insensitive 2 %{ 3 #include "stdio.h" 4 #include "tp.tab.h" 5 %} 6 7 %% 8 "{" {return '{';} 9 "}" {return '}';} 10 ";" {return ';';} 11 "create"

弹性代码:

  1 %option noyywrap nodefault yylineno case-insensitive
  2 %{
  3 #include "stdio.h"
  4 #include "tp.tab.h"
  5 %}
  6 
  7 %%
  8 "{"             {return '{';}
  9 "}"             {return '}';}
 10 ";"             {return ';';}
 11 "create"        {return CREATE;}
 12 "cmd"           {return CMD;}
 13 "int"           {yylval.intval = 20;return INT;}
 14 [a-zA-Z]+       {yylval.strval = yytext;printf("id:%s\n" , yylval.strval);return ID;}
 15 [ \t\n]
 16 <<EOF>>         {return 0;}
 17 .               {printf("mistery char\n");}
 18 
测试输出:

root@VM-Ubuntu203001:~/test/tpp# ./a.out t1.tp 
id:keeplive
id:a
20 , a;
id:b
20 , b;
keeplive
{
    int a;
    int b;
}
parse work!
我有两个问题:

1) 为什么第38行的操作会打印标记“;”?例如,“20,a;”和“20,b;”

2) 为什么第32行的操作会打印“keeplive” { INTA; int b; }“而不是简单的“保持活力”?

简短回答:

yylval.strval = yytext;
你不能那样使用
yytext
。它指向的字符串对于lexer是私有的,并且将在flex操作完成后立即更改。您需要执行以下操作:

yylval.strval = strdup(yytext);
然后,您需要确保在之后释放内存


详细回答:

yytext
实际上是指向包含输入的缓冲区的指针。为了使yytext像以NUL结尾的字符串一样工作,
flex
框架在执行操作之前用
NUL
覆盖标记后面的字符,然后在操作终止时替换原始字符。因此,
strdup
在操作内部可以正常工作,但在操作外部(在bison代码中),您现在有一个指针指向以令牌开头的缓冲区部分。之后情况会变得更糟,因为
flex
会将源代码的下一部分读入同一个缓冲区,现在指针指向随机垃圾。根据
flex
选项的不同,有几种可能的场景,但没有一种是漂亮的

所以黄金法则:
yytext
只在操作结束前有效。如果您想保留它,请复制它,然后确保在不再需要它时为该副本释放存储空间

在我编写的几乎所有lexer中,ID令牌实际上在符号表中找到标识符(或将其放在符号表中),并将指针返回到符号表中,这简化了内存管理。但是,对于例如字符串文本,您仍然存在基本相同的内存管理问题

yylval.strval = yytext;
yylval.strval = strdup(yytext);