2014-09-29 73 views
15

我发现,具有这种结构的C代码:Do..while内切换

switch (n) { 
    do { 
    case 1: 
     // do some things 
     if (some condition) 
      goto go_on; 
    case 2: 
     // do some things 
     if (some condition) 
      goto go_on; 
    case 3: 
     // do some things 
     if (some condition) 
      goto go_on; 
    } while (1); 
    do { 
    case 4: 
     // do some things 
     if (some condition) 
      goto go_on; 
    case 5: 
     // do some things 
     if (some condition) 
      goto go_on; 
    } while (1); 
} 

go_on: 

我用C编程年(很多年前),并会认为这将是一个语法错误。我认为这与优化循环有关,但我想知道是否有人可以解释它的作用。当到达while(1)时会发生什么,它是否会有效返回交换机?特别是为什么有两种做...在那里?

顺便说一下,我不要想开始讨论使用goto或如果这是不好的设计。我没有写这个,我假设作者这样做,因为它使循环运行尽可能快。

+13

看起来像[Duff's Device]的变体(https://en.wikipedia.org/wiki/Duff's_device)。这里有一个[解释](https://stackoverflow.com/questions/514118/how-does-duffs-device-work)。 – tangrs 2014-09-29 14:05:17

+2

将'case'语句看作标签,'switch'为'goto'。 – chux 2014-09-29 14:07:16

+1

这是一种循环展开技术。考虑循环结束。 – Alex 2014-09-29 14:09:54

回答

6

可能更好地显示程序流的等效代码。

类似于Duff's device允许在某个中间位置进入循环。 @ tangrs

这些日子一般被认为是1)编译器通常在优化&时做得更好2)正如OP发现的那样,可以很容易地隐藏代码的含义。谨慎使用。

在OP的代码中,在while条件之后,程序流程不会返回到switch语句。 switchcase仅影响初始进入while循环。

if (n == '1') goto case1; 
if (n == '2') goto case2; 
... 
if (n == '5') goto case5; 
goto go_on; 

do { 
    case1: 
    // do some things 
    if (some condition) goto go_on; 
    case2: 
    // do some things 
    if (some condition) goto go_on; 
    case3: 
    // do some things 
    if (some condition) goto go_on; 
} while (1); 

do { 
    case4: 
    // do some things 
    if (some condition) goto go_on; 
    case5: 
    // do some things 
    if (some condition) goto go_on; 
} while (1); 

go_on: 

[编辑]

有2个while环以容纳原来编码器的流跳进中间点到1 2的环路。

候选人重写如下。通过访问整体代码,当然可以有更清晰的解决方案。

int n2 = n; // Only evaluate n once as in the switch statement. 
if (n2 >= 1) { 
    if (n2 <= 3) { 
    while (1) { 
     if (n2 <= 1) { 
     // do some things 
     if (some condition) { break; } 
     } 
     if (n2 <= 2) { 
     // do some things 
     if (some condition) { break; } 
     } 
     // do some things 
     if (some condition) { break; } 
     n2 = 1; 
    } 

    else if (n2 <= 5) { 
    while (1) { 
     if (n2 <= 4) { 
     // do some things 
     if (some condition) { break; } 
     } 
     // do some things 
     if (some condition) { break; } 
     n2 = 4; 
    } 
    } 

} 
+0

谢谢,你和其他人回答。它现在非常有意义。事实上,我有点懊恼,我无法自己弄清楚。也许我对这个东西太老了! – kh99 2014-09-29 17:57:18

+0

@ kh99,怀疑是否过时。这项技术的作用很平滑,可以在非常有选择的情况下满足需要。但国际海事组织维护费用较高,原始编码和编码应避免难以维护这样的未公开片段。没有那么大的损失,以前没有来过。 – chux 2014-09-29 18:04:19

+0

我不确定第二个区块的“更清洁”部分。但我相信它的不对等。在第一个模块中,'n'只用于确定循环中的起始位置,并且在循环内不再重新检查。 – John 2014-09-29 19:59:46

6

让我为你重写这段代码,也许这会让它更加明显。下面的代码是给你贴一个或多或少相当于:

if (n == 1) goto ONE; 
if (n == 2) goto TWO; 
if (n == 3) goto THREE; 
if (n == 4) goto FOUR; 
if (n == 5) goto FIVE; 
goto SKIP_ALL; 

while (true) { 
ONE: 
    // do some things 
    if (some condition) goto go_on; 
TWO: 
    // do some things 
    if (some condition) goto go_on; 
THREE: 
    // do some things 
    if (some condition) goto go_on; 
} 

while (true) { 
FOUR: 
    // do some things 
    if (some condition) goto go_on; 
FIVE: 
    // do some things 
    if (some condition) goto go_on; 
} 

SKIP_ALL: 
go_on: 

的循环是在交换机中,它们不会导致开关更频繁发生。该开关基本上决定程序流跳转到哪个循环,以及该循环内的哪个指令开始。一旦跳到那里,循环正常继续。另请注意,一个开关通常比所有这些if语句更快。

不,一般来说goto并不坏设计。 A switch仅仅是一个转换而且开关并不坏设计。实际上,在CPU或VM内执行的函数/方法中的每个代码分支几乎都是一个简单的goto(有时是有条件的,有时不是)。只是goto是最原始的,低级的分支方式,并且告诉读者一点意图。无论何时出现更高一级的情况,一个让你的意图更明显的方法,最好使用那一个。使用goto只是不好的设计,如果你可以在不使用goto的情况下轻易地编写相同类型的代码,它也不会太糟糕。在一些(虽然非常罕见)的情况下,goto几乎是不可避免的,或者任何避免它的尝试都会产生丑陋,不可读,非常复杂的代码或者性能很差。

“goto被认为是有害的”文章来自某个时代,其中有些人正在使用goto做所有事情:对于if/else代码分支,for循环,开关,突破循环/开关,以避免递归,等等。如果你过度使用像这样的跳转,并且如果你跳转到跳转到另一个标签的标签,那么人们会失去概览。这样的代码是不可读的,并且极难调试。这就是你如何编写汇编代码的方式,但这不应该是我们编写C代码的方式。

1

所有情况都是标签。因此,如果去除switch语句的代码看起来像

do { 
    case '1': 
     // do some things 
     if (some condition) 
      goto go_on; 
    case '2': 
     // do some things 
     if (some condition) 
      goto go_on; 
    case '3': 
     // do some things 
     if (some condition) 
      goto go_on; 
    } while (1); 
    do { 
    case '4': 
     // do some things 
     if (some condition) 
      goto go_on; 
    case '5': 
     // do some things 
     if (some condition) 
      goto go_on; 
    } while (1); 

go_on: 

因此有两个普通无限DO-while循环即停止取决于环路一些内部条件的迭代。你可以写这样的代码更简单

do { 
     // do some things 
     if (some condition) 
      goto go_on; 
     // do some things 
     if (some condition) 
      goto go_on; 
     // do some things 
     if (some condition) 
      goto go_on; 
    } while (1); 

    do { 
     // do some things 
     if (some condition) 
      goto go_on; 
     // do some things 
     if (some condition) 
      goto go_on; 
    } while (1); 

go_on: 

那么,是什么的开关添加到上面显示的代码?它仅为绕过循环正常入口点的循环提供入口点。这是第一次迭代将从一些案例标签开始,仅此而已。此外,如果没有适当的标签,那么这些循环将被跳过。

0

我不知道如果我的变化是更好的,但我会写

switch (n) { 
    LOOP1: 
     // do some things 
     if (some condition) 
      break; 
    case 2: 
     // do some things 
     if (some condition) 
      break; 
    case 3: 
     // do some things 
     if (some condition) 
      break; 
     goto LOOP1; 
    case 4: 
    LOOP4: 
     // do some things 
     if (some condition) 
      break; 
    case 5: 
     // do some things 
     if (some condition) 
      break; 
     goto LOOP4; 
} 

原来是因为switch-do-while的非常令人迷惑。所有替换解决方案都使用一些goto,实际上其中很多和/或嵌套复杂的条件。所以我用goto替换了令人困惑的do-while


您可以简化

 if (some condition) 
      break; 
     goto LOOP4; 

 if (!some condition) goto LOOP4; 

但我更愿意把它制服。


如果你可以提取

// do some things 
    if (some condition) 
     break; 

成功能,你可以写

switch (n) { 
    case 1: while (true) { 
     if (body1()) break; 
     if (body2()) break; 
     if (body3()) break; 
    } 
    break; 
    case 2: while (true) { 
     if (body2()) break; 
     if (body3()) break; 
     if (body1()) break; 
    } 
    break; 
    case 3: while (true) { 
     if (body3()) break; 
     if (body1()) break; 
     if (body2()) break; 
    } 
    break; 
    case 4: while (true) { 
     if (body4()) break; 
     if (body5()) break; 
    } 
    break; 
    case 5: while (true) { 
     if (body5()) break; 
     if (body4()) break; 
    } 
} 

这是有点重复,但很明显,它不使用秋天,虽然。

+0

@chux谢谢,修正。但现在,它更加重复。 – maaartinus 2014-09-29 21:12:21

+0

确实如此 - 但至少肯定很清楚。 – chux 2014-09-29 21:13:06

0

我以前见过这种结构。作者可能正在编写某种状态机器。 (请参阅zlib的源代码示例。)

是否将“做某些事情”部分更改为n以适应接下来要运行的任何情况?

+0

我在看的原始代码在这里:[link](http://wwwhomes.uni-bielefeld.de/achim/prime_sieve.html)。正如你所看到的,这是一个Eratosthenes实施的筛选。我不相信循环中的n被改变了。 – kh99 2014-09-30 15:44:47