在研讀《C Primer Plus 第五版》中文版時,筆者發現第 17 章(第 499-500 頁)的兩個示例函數 ListItemCount() 和 Traverse() 存在指針層級不匹配的錯誤。這個問題若不修正,會導致程序運行時發生段錯誤或邏輯錯誤。
原書 list.c 中這兩個函數的定義如下:
unsigned int ListItemCount(const List * plist)
{
unsigned int count = 0;
Node * pnode = *plist; // 此處解引用 plist
while(pnode != NULL)
{
++count;
pnode = pnode->next;
}
return count;
}
void Traverse(const List * plist, void(*pfun)(Item item))
{
Node * pnode = *plist; // 同樣解引用 plist
while(pnode != NULL)
{
(*pfun)(pnode->item);
pnode = pnode->next;
}
}
然而,在 film3.c 主程序中調用這兩個函數時,傳遞的是 List 類型變量 movies 的地址(即指針):
Traverse(movies, showmovies); // 傳遞指針
printf("You entered %d movies.\n", ListItemCount(movies)); // 傳遞指針
錯誤根源:函數參數聲明為 const List * plist(指向指針的指針),但調用時傳遞的是 List 類型的指針(即 Node *)。函數內部通過 *plist 解引用,導致實際操作的是指針指向的內容而非鏈表頭節點,造成類型不匹配。
Traverse(&movies, showmovies);
printf("You entered %d movies.\n", ListItemCount(&movies));
unsigned int ListItemCount(const List * plist)
{
unsigned int count = 0;
Node * pnode = plist; // 直接使用 plist,不解引用
while(pnode != NULL)
{
++count;
pnode = pnode->next;
}
return count;
}
void Traverse(const List * plist, void(*pfun)(Item item))
{
Node * pnode = plist; // 直接使用 plist
while(pnode != NULL)
{
(*pfun)(pnode->item);
pnode = pnode->next;
}
}
方案二更符合 C 語言慣用模式:List 本身就是指向鏈表頭節點的指針(typedef Node * List),函數參數應直接接收該指針,而非指針的指針。這也是原書英文版的正確做法,中文版在翻譯或排版時可能引入了此錯誤。
此錯誤屬於典型的「指針層級混淆」問題。建議讀者在參考示例代碼時,務必核對函數參數聲明與調用處的實際傳遞類型是否一致。採用方案二修改函數定義可從根本上消除該隱患,也更符合鏈表操作的直觀邏輯。