达夫设备 Duff's Device

达夫设备 Duff's Device

Content #

send(to, from, count)
register short *to, *from;
register count;
{
    register n = (count + 7) / 8;
    switch (count % 8) {
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
            } while (--n > 0);
    }
}

达夫把正常情况下分开写的 switch 语句和 while 语句整合在了一起,形成了令人疑惑的 switch 和 while 交错在一起的代码,但是代码量减少了将近一半。正常情况下我们会将 case 语句的作用域视作一个单独的代码块(block),而达夫设备表明实际上并不是这样。所以为什么可以这样写呢?

我们可以查阅最新的 C17 草案 PDF 版本,直接跳转到在 6.8 Statements and blocks,可以看到 case 语句属于 Labeled statements(带标签的语句:

labeled-statement:
    identifier : statement
    case constant-expression : statement
    default : statement

并且任何一条语句都能在自己前面声明一个标识符作为标签名称,标签本身并不会改变控制流,控制流在它们之间执行不受阻碍: Any statement may be preceded by a prefix that declares an identifier as a label name. Labels in themselves do not alter the flow of control, which continues unimpeded across them.

达夫设备的巧妙之处在于,它巧妙地利用了 Labeled statements 不改变控制流的语法定义;而正常情况下我们会将 case 语句的作用域视作一个单独的代码块,仅仅是 code style 的最佳实践而已,并不是编译器强约束的。

当达夫设备开始运行时,会先根据 switch 匹配到对应的 case 语句,由于没有声明 break 所以会一路向下执行(falls through)直到被 while 捕获,进入循环逻辑。

From #

https://zhuanlan.zhihu.com/p/284223705