typedef struct BiTNode {
char data;
struct BiTNode *lchild, *rchild;
} BiTNode, *BiTree;
BiTree CreateBiTree() {
char ch;
BiTree T;
scanf("%c", &ch);
if (ch == '#') T = NULL;
else {
T = (BiTree)malloc(sizeof(BiTNode));
T->data = ch;
T->lchild = CreateBiTree();
T->rchild = CreateBiTree();
}
return T; // 回傳區域指標變數 T
}
不是錯誤,這是標準且安全的做法。
| 情境 | 說明 | 是否安全 |
|---|---|---|
| 回傳區域陣列/結構體的位址 | 函數結束時棧框銷毀,記憶體失效 | ❌ 不安全 |
回傳 malloc/new 配置的堆疊記憶體位址 | 記憶體生命週期由呼叫者管理,函數結束不釋放 | ✅ 安全 |
在 CreateBiTree 中:
T 是區域指標變數(儲存在棧上)malloc 在堆疊上配置 BiTNode 記憶體T 指向該堆疊記憶體Node* createNode()、Tree* buildTree()、List* initList()fopen 回傳 FILE*、malloc 回傳 void*、strdup 回傳 char*BiTree root = CreateBiTree();
// ... 使用樹 ...
DestroyBiTree(root); // 遞迴 free 每個節點
void DestroyBiTree(BiTree T) {
if (T) {
DestroyBiTree(T->lchild);
DestroyBiTree(T->rchild);
free(T);
}
}
若呼叫者忘記 free,會造成記憶體洩漏;若重複 free,會導致未定義行為。
typedef struct {
BiTree root;
size_t nodeCount;
} BiTreeResult;
BiTreeResult CreateBiTreeSafe() {
BiTreeResult res = {0};
res.root = CreateBiTree(); // 原函數
// 可額外計算節點數
return res; // 結構體由值回傳,無指標問題
}
現代 C 實務常建議:
NULL 表示失敗InOrderTraverse 和 PostOrderTraverse 內部錯誤呼叫 PreOrderTraverse,應分別呼叫自己。main 回傳型別應為 int,非 void。BiTree *t1; t1 = &T; 多餘,未使用。void InOrderTraverse(BiTree T) {
if (T) {
InOrderTraverse(T->lchild);
printf("%c", T->data);
InOrderTraverse(T->rchild);
}
}
void PostOrderTraverse(BiTree T) {
if (T) {
PostOrderTraverse(T->lchild);
PostOrderTraverse(T->rchild);
printf("%c", T->data);
}
}
回傳 malloc 配置記憶體的指標是正確、標準、廣泛使用的 C 語言慣用語。關鍵在於:配置在堆疊、回傳位址值、文件化擁有權移交、呼叫者負責釋放。原始碼的真正問題在遍歷函數邏輯錯誤與 main 簽名,而非指標回傳。