我寫了個小routine要parse字串, 把"aaa_bbbb_cc"拆成"aaa", "bbbb", "cc", 其中"_"代表切分用的字元, 程式碼大概如下(忽略buffer size checking等細節)
void ParseString(char* pSource, char* pDest, const charcSeparator) { while(true) { if(*pSource == '\0' || *pSource == cSeparator) break; *pDest = *pSource; pDest++; pSource++; } *pDest = '\0'; } void InCallerFunction() { char Buf[256]; char* pSource = "String to be parsed"; while(*pSource != '\0') { ParseString(pSource, Buf, ' '); //這裡用空白字元作切分 DoSomeThing(Buf); } }
Compile沒問題, 但一跑起來只有第一個字串被抓出來, 而且陷入無窮迴圈.
原來我想透過改變 pSource 所指到的位置, 指到結尾就結束 while loop, 但 pSource 沒有改變. 原因是我的 pSource 是以 call-by-value 的方式傳進去的. 這個錯誤很像下面這個版本:
void IncreaseI(int i) { i++; } void main() { int i = 0; IncreaseI(i); // i still == 0 }
main() 中的 i 不會被 IncreaseI() 中的 i++ 給改變, 因為 IncreaseI() 當中改變的只是 i 的分身. 應該改成:
void IncreaseI(int* i) { (*i)++; }
所以要修正 ParseString 的問題只要改成傳"指向字串的指標的指標"進去即可:
void ParseString(char** ppSource, char* pDest, const charcSeparator) { char* pSource = *ppSource; while(true) { if(*pSource == '\0' || *pSource == cSeparator) break; *pDest = *pString; pDest++; pString++; } *pDest = '\0'; *ppSource = pSource; }
PS:裡面的字還有辦法加粗體或帶顏色的屬性嗎?
我們都用 SyntaxHighlighter
回覆刪除我暫時先幫你改成 => pre class="brush: cpp; wrap-lines: false;"
這是一些可調屬性
回覆刪除感謝分享! ,第一時間還真不容易看出來問題在哪(關鍵在邏輯上希望ParseString裡讓source pointer 一起跟著跑)。
回覆刪除ps: ParseString裡的 pBuf 應該改成 pDest?
Thanks James!
回覆刪除Hi Abow, 是低 pBuf已改正為 pDest
謝啦
作者已經移除這則留言。
回覆刪除之前在看 Refactoring 時, 有提到因為 Java 用的是 Pass by Value,
回覆刪除所以作者, Martin Fowler, 認為這類傳進 method 的 parameter, 應該在 method 內用其他 temporarily variable 來接他的值, 之後 method 內都用 temporarily variable 以避免混淆和誤用. 甚至在 method 宣告, 該 parameter 也可以直接宣告成 final 來限制其使用行為.
Item:
Remove Assignments to Parameters