2011-11-16 27 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和默认是相似的,它们全都落入默认逻辑(这是忽略)。

它并不总是一种很好的代码方式,但如果它被很好地使用,它可以更好地呈现逻辑。