在看 apue 第 21 章 與網路印表機通信一章時,發現一段關於鏈表操作的代碼有問題,現在摘出來讓大家 review 一下。先上代碼: printd.c 這是列印服務的源代碼,在列印時,用戶通過 print 命令提交待列印的文件,print 命令通過 tcp 與 printd 服務通訊, 將文件及 ...
在看 apue 第 21 章 與網路印表機通信一章時,發現一段關於鏈表操作的代碼有問題,現在摘出來讓大家 review 一下。先上代碼:
這是列印服務的源代碼,在列印時,用戶通過 print 命令提交待列印的文件,print 命令通過 tcp 與 printd 服務通訊,
將文件及列印相關的參數傳遞給後者;對於每個客戶,printd 服務會創建一個 worker 結構節點,
放在一個由 workers 變數指定了頭的雙向鏈表中。所以這段代碼本質上就是簡單的雙向鏈接操作:
1 void add_worker (pthread_t tid, int sockfd) 2 { 3 struct worker_thread *wtp; 4 if ((wtp = malloc (sizeof (struct worker_thread))) == NULL) { 5 log_ret ("add_worker: can't malloc"); 6 pthread_exit ((void *)1); 7 } 8 9 wtp->tid = tid; 10 wtp->sockfd = sockfd; 11 12 log_msg ("prepare to add worker"); 13 pthread_mutex_lock (&workerlock); 14 15 wtp->prev = NULL; 16 wtp->next = workers; 17 if (workers == NULL) 18 workers = wtp; 19 else 20 workers->prev = wtp; 21 22 pthread_mutex_unlock (&workerlock); 23 }
重點就是 15-20 這 6 行啦,當第一次加入節點時, workers 為 NULL,所以走第一個條件分支,這沒有問題;
但是再加入節點時, workers 不為 NULL,此時走 else 分支,將當前頭的上一個節點設置為待插入的新節點 wtp,
到現在還好,可是等等,怎麼就沒下文了?!這個節點還沒完全加入鏈表呢……
正確的做法應該是在結尾前再加一句:
else { workers->prev = wtp; workers = wtp; }
這樣才能算完嘛。道理就不多說了,不信自己畫個鏈表看看。下麵給出優化後的完整代碼:
1 void add_worker (pthread_t tid, int sockfd) 2 { 3 struct worker_thread *wtp; 4 if ((wtp = malloc (sizeof (struct worker_thread))) == NULL) { 5 log_ret ("add_worker: can't malloc"); 6 pthread_exit ((void *)1); 7 } 8 9 wtp->tid = tid; 10 wtp->sockfd = sockfd; 11 pthread_mutex_lock (&workerlock); 12 13 wtp->prev = NULL; 14 wtp->next = workers; 15 if (workers != NULL) 16 workers->prev = wtp; 17 18 workers = wtp; 19 20 pthread_mutex_unlock (&workerlock); 21 }
好吧,我承認作為經典著作也會有這種低級錯誤。
今天的吹毛求疵就到這裡,作為一個有職業素養的程式員,不在雞蛋里挑出骨頭來不罷休,嘿嘿……