[英]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.