使用通用堆疊庫(Piles.h/.c)存放 Point 結構體時,整數測試正常,但結構體連續 pop 卻總顯示最後壓入的資料。程式在 Linux GCC 下編譯通過,執行結果如下:
Empiler le point (0.000000, 0.000000)
Empiler le point (1.000000, 1.000000)
Empiler le point (2.000000, 2.000000)
Empiler le point (3.000000, 3.000000)
Depiler le point (3.000000, 3.000000) // 重複四次!
Depiler le point (3.000000, 3.000000)
Depiler le point (3.000000, 3.000000)
Depiler le point (3.000000, 3.000000)
typedef struct _pile *Pile;
void empiler(Pile, void *element, ExceptionPile *); // push
void depiler(Pile, ExceptionPile *); // pop
void *sommet(Pile, ExceptionPile *); // top
Point *p1 = malloc(sizeof *p1);
for (i = 0; i < 4; i++) {
p1->x = (float)i;
p1->y = (float)i;
empiler(p_nouveau, p1, &exp); // 每次傳入同一指標!
}
for (i = 0; i < 4; i++) {
p1 = sommet(p_nouveau, &exp);
printf("Depiler le point (%f, %f)\n", p1->x, p1->y);
depiler(p_nouveau, &exp);
}
經典指標陷阱:迴圈中重複使用同一個 p1 指標,每次 push 存入的都是同一個記憶體位址。迴圈結束時,該記憶體保留最後賦值(3,3),堆疊四個節點全指向同一塊記憶體,故 pop 時全讀到 (3,3)。
整數測試之所以「正常」,是因為 element = (int*)i 將整數值強轉為指標,每次傳入的「指標值」不同,實際上是把整數編碼在指標裡,而非真正指向獨立記憶體。
for (i = 0; i < 4; i++) {
Point *p1 = malloc(sizeof(Point)); // 每次新建節點!
p1->x = (float)i;
p1->y = (float)i;
empiler(p_nouveau, p1, &exp);
}
// pop 時記得 free
for (i = 0; i < 4; i++) {
Point *p1 = sommet(p_nouveau, &exp);
printf("Depiler le point (%f, %f)\n", p1->x, p1->y);
depiler(p_nouveau, &exp);
free(p1); // 釋放記憶體
}
| 法文 | 中文 | 英文 |
|---|---|---|
| pile | 堆疊 | stack |
| empiler | 壓入 | push |
| depiler | 彈出 | pop |
| sommet | 堆疊頂 | top |
| element | 元素 | element |
通用資料結構以 void* 存放任意型別時,呼叫端必須確保每個元素擁有獨立生命週期。此問題非庫本身缺陷,屬於 C 語言指標語意的常見誤區。