达夫设备以及switch case

来源:互联网 发布:js递归遍历json树 编辑:程序博客网 时间:2024/05/17 02:53

神奇的Duff Device,这就是艺术!

以下是一个利用达夫设备拷贝字符串的代码:

 

inline void duffdevice_strcpy(char* dest, const char* source)
{
 size_t  length = strlen(source);
 register size_t n = length/8;
 switch(++length%8) // 字符串长度加一,目的是为了拷贝source中的'/0'
 {
 case 0: while(n-- > 0){ *dest++ = *source++;
 case 7: *dest++ = *source++;
 case 6: *dest++ = *source++;
 case 5: *dest++ = *source++;
 case 4: *dest++ = *source++;
 case 3: *dest++ = *source++;
 case 2: *dest++ = *source++;
 case 1: *dest++ = *source++;
  }
 }
}

 

这段代码看似很难理解,其实很简单。首先计算出source的长度length,将length个字符每八个分为一组,而其中有一组可能不满8个(为length除以8的余数个)。变量n记录了包含8个字符的组的数量。而在switch中,计算出length除以8的余数。若不为零,直接跳进循环体,执行对应的case语句。由于没有break语句,因此该case语句其后所有的case都被执行(fall through)。执行完毕时,不满8个字符的那一组正好被拷贝。由于在循环体内,因此回过头来,继续循环,利用fall through,每次循环拷贝一组(8个)字符,直到所有字符(n组)被拷贝。

达夫设备的核心思想是减少循环条件的比较次数。如果循环体内的语句很简单,过多的比较则会严重影响性能。例如上述拷贝字符串的例子,即便传统的经典while循环 while((*dest++ = *source++) != '/0'); ,每拷贝一个字符,就需要进行一次比较,考虑到拷贝字符的开销很小,因此整个执行过程中,循环条件的判断开销占了很大的比例。而达夫设备通过分组,将比较次数减少了7/8,明显的提高了效率。

注意到变量n声明为register,存入寄存器中。一般内存中的数据,在使用时先要转到寄存器中,使用完毕在从寄存器转回内存中。由于n多次使用,将其一直放于寄存器中,进一步提高效率。达夫设备真是要把所有效率都给榨出来!

从达夫设备联想到switch case的一点琐碎但有趣的语法。

首先就是fall through了。在switch中,一旦找到匹配的case,执行完毕后,其后的所有case都要执行,因此一般需要显示地用break跳出switch。case可以想象成一个标签而已,一旦找到了一个标签,其后的标签全部去掉,对应的语句就和普通的语句没有什么区别了,按顺序执行,除非遇到break。

此外,需要注意的是case后必须是整型常量。浮点型或者非常量整型都是错误的。

另一个需要注意的是case后的变量声明和定义。如下:

switch(ival)
{
case 0int a = 0;
case 1:
//……
}

倘若这种变量声明合法,此时,变量a由于与case 1等在同一个语句块中,在case 1中是可见的。但在case 1中使用a时,a很可能是未初始化的,这就成为错误的源泉。因此,编译器将这种语法标记为错误。

以下语法是正确的,此时,a在其他case中是不可见的,保证了安全。

switch(ival)
{
case 0:
        {
                
int a = 0;
        }
case 1:
//……
}

即便如switch case般貌似简单的语法,也有很多琐碎的地方,C++真是博大精深。吾将继续求索!

原创粉丝点击