You Don’t Have to Resort to Goto
Posted on June 19th, 2010 at 22:35 by fr3@K

你一定看過這樣的 code:

    C:
    1. int do_something(size_t n)
    2. {
    3.   int result = ERROR;
    4.   char* buf;
    5.  
    6.   if(n)
    7.   {
    8.     buf =  malloc(n);
    9.     if(buf != 0)
    10.     {
    11.       if(do_foo(buf, n) == OKAY)
    12.       {
    13.         if(do_bar(buf, n) == OKAY)
    14.           result = OKAY;
    15.       }
    16.       free(buf);
    17.     }
    18.   }
    19.  
    20.   return result;
    21. }

可讀性差的巢狀 if block, 只為了堅持由一處 return.

想必也看過這樣的 code:

    C:
    1. int do_something(size_t n)
    2. {
    3.   char* buf;
    4.  
    5.   if(n == 0)
    6.     return ERROR;
    7.  
    8.   buf =  malloc(n);
    9.   if(buf == 0)
    10.     return ERROR;
    11.  
    12.   if(do_foo(buf, n) != OKAY)
    13.   {
    14.     free(buf);
    15.     return ERROR;
    16.   }
    17.  
    18.   if(do_bar(buf, n) != OKAY)
    19.   {
    20.     free(buf);
    21.     return ERROR;
    22.   }
    23.  
    24.   return OKAY;
    25. }

由多處 return 雖避掉了巢狀 if block, 但需要在多個地方 cleanup 之前所請求的資源.

當然還有這樣的 code:

    C:
    1. int do_something(size_t n)
    2. {
    3.   int result = ERROR;
    4.   char* buf = 0;
    5.  
    6.   if(n == 0)
    7.     goto CLEANUP;
    8.  
    9.   buf =  malloc(n);
    10.   if(buf == 0)
    11.     goto CLEANUP;
    12.  
    13.   if(do_foo(buf, n) != OKAY)
    14.     goto CLEANUP;
    15.  
    16.   if(do_bar(buf, n) != OKAY)
    17.     goto CLEANUP;
    18.  
    19.   result = OKAY;
    20.  
    21. CLEANUP:
    22.   // free()ing a NULL ptr is a no-op.
    23.   free(buf);
    24.  
    25.   return result;
    26. }

不管老師以前是如何叮嚀不要用 goto, 為了閃掉那噁心的巢狀 if block 以及重複的 code, 只好還是用了 goto.

其實還有另一種選擇, 不用重複寫釋放資源的 code, 也不需要用到 goto:

    C:
    1. static int do_something_helper(char* buf, n)
    2. {
    3.   if(do_foo(buf, n) != OKAY)
    4.     return ERROR;
    5.  
    6.   if(do_bar(buf, n) != OKAY)
    7.     return ERROR;
    8.  
    9.   return OKAY;
    10. }
    11.  
    12. int do_something(size_t n)
    13. {
    14.   int result;
    15.   char* buf;
    16.  
    17.   if(n == 0)
    18.     return ERROR;
    19.  
    20.   buf =  malloc(n);
    21.   if(buf == 0)
    22.     return ERROR;
    23.  
    24.   result = do_something_helper(buf, n);
    25.  
    26.   free(buf);
    27.   return result;
    28. }

一旦把 resource management 跟 logic 的部份拆開, code 就好看多了.

    獻給一位因為沒有充足理由的規定, 卻在工作中被迫得寫 pure C 的可憐同事.

後記: 這應該是目前我寫的唯一一篇 pure C 的文字. 擔心它太寂寞, 還是把它打上了 C++ 的 tag.

del.icio.us:You Don't Have to Resort to Goto digg:You Don't Have to Resort to Goto spurl:You Don't Have to Resort to Goto newsvine:You Don't Have to Resort to Goto furl:You Don't Have to Resort to Goto Y!:You Don't Have to Resort to Goto 黑米共享書籤:You Don't Have to Resort to Goto 推推王:You Don't Have to Resort to Goto
Previous Post
« 華碩品質, 以卵擊石 «
Next Post
» Did You Know, “new (nothrow) T” Throws? »

8 Comments »

Comment #13747

原來是只能用 C++ C (ed: must have been a typo of av – fr3@K),我看到一半還在想怎麼沒 smart pointer. 不過話說這種利用增加抽像性來解決問題的手法還真是到處都管用啊.

Comment by av — June 20, 2010 @ 0:17


Comment #13749

寫這篇又讓我再度感受到 pure C 的 單調 簡潔啊~ XD

Comment by fr3@K — June 20, 2010 @ 2:31


Comment #13752

這不就是 Extract Method ? XD

Comment by clsung — June 20, 2010 @ 13:16


Comment #13754

@clsung,

剛 google 了, 才知道啥是 Extract Method. :-P

對, 的確就是 Extract Method. :-D

Comment by fr3@K — June 20, 2010 @ 16:28


Comment #13757

do { … } while (0) + break 也不錯啊~ :D

Comment by Caiyu — June 21, 2010 @ 10:23


Comment #13759

@Caiyu,

是可以, 但 Extract Method (IMO) 有更多優點.

例如, 把文中最後一個例子的 do_something_helper 重新命名為 do_something_using_client_buffer, 就更接近 code 即 comment.

Comment by fr3@K — June 21, 2010 @ 17:45


Comment #13822

我記得剛來時我用do {} while (0) + break, 好像被architect說很怪..XD 有不好的地方嘛?

Comment by starryalley — July 2, 2010 @ 14:57


Comment #13836

@starryalley,

我也覺得是有點怪.

在上個回應中說的 “可以” 是指功能上的可行性. 只是這樣的 code 很顯然較不容易閱讀, 還是應該避免.

Comment by fr3@K — July 4, 2010 @ 22:36


Comments RSS TrackBack URI

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>