簡體   English   中英

Bison遞歸與鏈接列表的行為異常

[英]Bison recursion behaving strangely with linked list

我有一個類似鏈表的結構:

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 ,然后在主函數中按以下方式分配: 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的需要,因此有可能正確組合一個復合語句。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM