c - Bison 递归在链表中表现异常

标签 c parsing bison yacc bnf

我有一个类似链表的结构:

typedef struct NODE
{
  NODE_TYPES type;
  struct NODE* curr;
  struct NODE* next;
} NODE;

我有这个递归规则:

lines: stmt             {
                            root -> next = $1;
                            $1 -> next = NULL;
                            $$ = $1;

                        }
| lines stmt            {
                            $1 -> next = $2;
                            $2 -> next = NULL;
                            $$ = $2;

                        }
;

stmt 始终是指向 NODE 结构的指针

root 被定义为 NODE *root 之后,在 main 函数中,这样分配:root = malloc(sizeof(NODE));

但是,从 root 开始递归地打印我的节点列表,我只得到最后一个节点。为什么我会丢失 root 和最后一个节点之间的数据?

最佳答案

显然,问题在于您将 stmt 的语义值设置为指向堆栈分配的局部变量。由于局部变量的生命周期在语义操作完成后立即到期,因此该值是一个悬空指针并且使用它是未定义的行为。 (但在这种情况下,您可能会发现所有指针都指向同一个内存块,每次重复使用该特定堆栈帧时,其内容都会发生变化。)

您需要确保 stmt 的值是指向新分配的 NODE 的指针:

stmt : /* ... something ... */ 
                  { $$ = malloc(sizeof *$$);
                    $$ = (NODE){/* ... */, .next = NULL; }

这很重要。如果没有某种堆分配新 NODE 的函数,就没有好的方法可以做到这一点。您可以尝试通过保留一个 NODE 池并自己回收它们来减少 malloc 开销,但是由于大多数现代 malloc 实现在回收小型固定大小分配方面做得很好,这是过早的优化。

链表代码适用于简单的语句,但依靠全局变量来维护链表头部的可扩展性不是很好。这将使解析复合语句(例如条件和循环)变得不可能,因为您最终将重用 root 作为内部语句列表。最好以自然的方式构建列表(这将是倒退的)并在最后反转它:

stmts: stmt       /* Default action is fine */
     | stmts stmt { $$ = $2; $$->next = $1; }
line : stmts      { $$ = reverse($$); }

在这个简单的代码中,每次 stmts: stmts stmt 减少时,它都会将新的 NODE 推到列表的前面,这显然是反向创建列表命令。所以当 line 减少时(这将在所有 stmt 被解析之后),列表需要被反转。可以原地反转:

NODE* reverse(NODE* last) {
  NODE* next = NULL;
  while (last) {
    NODE* tmp = last->next; 
    last->next = next;
    next = last;
    last = tmp;
  }
  return next;
}

这完全消除了对全局 root 的需要,因此可以正确组装复合语句。

关于c - Bison 递归在链表中表现异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55447047/

相关文章:

使用 sscanf 检查空格长度

java - 在 ll(1) 中检查一种语法与另一种语法的算法

visual-studio-2010 - bison.exe 找不到 VS2010 中的 MSBuild 所需的文件 m4sugar.m4

c - C 中函数维护内部状态

c - 程序有时被标记为病毒

c - 为什么程序没有正确获取输入

python - 索引错误: list index out of range CSV parser

c++ - SQL 解析器库 - 从查询中获取表名

c - 正则表达式匹配行(换行符除外)(FLEX、BISON)

bison - Flex 2.5.35 yy_scan_buffer 未初始化行号和列号