2011-11-16 31 views
5

今天我遇到了一個病例切換代碼片段,有點驚訝,看它是如何工作的。該代碼是:案例切換是否像這樣工作?

switch (blah) 
{ 
case a: 
    break; 
case b: 
    break; 
case c: 
case d: 
case e: 
    { 
    /* code here */ 
    } 
    break; 
default : 
    return; 
} 

要我在這裏的變量是c場景驚訝的是,路徑中的「這裏代碼」段裏面去了。我同意在案件開關的c部分末尾沒有任何中斷,但我會想象它會通過default代替。當您登陸case blah:線路時,是否檢查您的當前值是否與特定案例相匹配,然後是否允許您進入特定細分市場?否則,有什麼意義的案件?

回答

13

這就是所謂的情況下落空,並且是期望的行爲。它允許你在案例之間共享代碼。

如何使用情況下通行爲的一個例子:

switch(blah) 
{ 
case a: 
    function1(); 
case b: 
    function2(); 
case c: 
    function3(); 
    break; 
default: 
    break; 
} 

如果進入交換機時blah == a,那麼你將執行function1()function2()function3()

如果你不想要這種行爲,你可以通過包括break聲明來退出。

switch(blah) 
{ 
case a: 
    function1(); 
    break; 
case b: 
    function2(); 
    break; 
case c: 
    function3(); 
    break; 
default: 
    break; 
} 

switch語句的工作方式是,它會(或多或少)執行goto跳轉到你的case標籤,並保留從該點運行。當執行命中break時,它將離開開關塊。

+0

: - 感謝您的精心答覆!我確實知道它是如何工作的,它只是反對我的常識而已。我仍然認爲這種行爲是違反直覺的。我一直認爲那些「案例(blah)」有點像檢查,以確保如果你的變量確實是(blah)..明顯不是C的工作原理! – Manish

+2

@Manish:開關通常使用「[分支表](http://en.wikipedia.org/wiki/Branch_table)」來實現,這樣也可以幫助您理解。編程中不存在常識,順便說一句。你需要「程序員的感覺」而不是:) –

+2

@Manish這兩種行爲都是可取的,如果需要的話,可以有默認的貫穿和顯式的「break」,或者默認分手並明確地繼續。從常識的角度來看,後者似乎更自然,但考慮到實施,默認穿透是更自然的,因此被選中。 –

10

這是正確的行爲,它被稱爲「通過」。這讓您可以使用相同的代碼處理多個案例。在高級情況下,您可能希望在一種情況下執行一些代碼,然後再轉到另一種情況。

人爲的例子:

switch(command) 
{ 
    case CMD_SAVEAS: 
    { 
     this->PromptForFilename(); 
    } // DO NOT BREAK, we still want to save 
    case CMD_SAVE: 
    { 
     this->Save(); 
    } break; 


    case CMD_CLOSE: 
    { 
     this->Close(); 
    } break; 

    default: 
     break; 
} 
+1

跌倒的着名例子是[Duff's device](http://en.wikipedia.org/wiki/Duff%27s_device)。 –

+0

這是一個非常酷的用法,但我想象這是編譯器通常會爲您優化速度時爲您做的事情。除非有一個非常好的理由和一個適當的評論塊來解釋爲什麼有必要以及如何維護它,否則我絕不會希望看到用C寫的東西。 – shenles

2

幸運的是,C++不依賴於你的想象:-)

認爲開關標籤爲「轉到」標籤,並switch(blah)簡單地「變爲」相應的標籤,然後將代碼只是從那裏流。

2

其實switch語句按照您觀察的方式工作。它的設計使您可以將多個案例組合在一起,直到遇到中斷並且它像篩子一樣。

這裏是我的一個項目一個真實的例子:

struct keystore_entry *new_keystore(p_rsd_t rsd, enum keystore_entry_type type, const void *value, size_t size) { 
     struct keystore_entry *e; 
     e = rsd_malloc(rsd, sizeof(struct keystore_entry)); 
     if (!e) 
       return NULL; 
     e->type = type; 
     switch (e->type) { 
     case KE_DOUBLE: 
       memcpy(&e->dblval, value, sizeof(double)); 
       break; 
     case KE_INTEGER: 
       memcpy(&e->intval, value, sizeof(int)); 
       break; 

     /* NOTICE HERE */ 

     case KE_STRING: 
       if (size == 0) { 
         /* calculate the size if it's zero */ 
         size = strlen((const char *)value); 
       } 
     case KE_VOIDPTR: 
       e->ptr = rsd_malloc(rsd, size); 
       e->size = size; 
       memcpy(e->ptr, value, size); 
       break; 

     /* TO HERE */ 
     default: 
       return NULL; 
     } 
     return e; 
} 

KE_STRINGKE_VOIDPTR案件的代碼是除了大小的字符串的情況下的計算相同。

4

這被稱爲跌落。

它正在做你所看到的:幾個案例將執行相同的一段代碼。

也正是在做額外處理某種情況下方便,某些共享邏輯:

// psuedo code: 
void stopServer() { 
    switch (serverStatus) 
    case STARTING: 
    { 
     extraCleanUpForStartingServer(); 
     // fall-thru 
    } 
    case STARTED: 
    { 
     deallocateResources(); 
     serverStatus = STOPPED; 
     break; 
    } 
    case STOPPING: 
    case STOPPED: 
    default: 
     // ignored 
     break; 
} 

這是在開關情況下的典型的使用落空的。在STARTING和STARTED的情況下,我們需要執行deallocateResources並將狀態更改爲STOPPED,但是STARTING需要一些額外的清理。通過上述方法,您可以清楚地在「啓動」中顯示「通用邏輯」和額外的邏輯。

STOPPED,STOPPING和默認是相似的,它們全都落入默認邏輯(這是忽略)。

它並不總是一種很好的代碼方式,但如果它被很好地使用,它可以更好地呈現邏輯。