The C++ Programming Language课后习题达夫设备解析
代码
首先放上代码:
void send( int * to, int * from, int count)
// Duff设施,有帮助的注释被有意删去了
{
int 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 );
}
}
当当当~解析
首先是对怪诞的do-while循环结构和switch-case语句的杂糅感到迷惑:这个程序的循环入口到底是哪里??!!(迷惑)
这个问题先搁置。我先解决了其他的一些小的问题,最后进行说明总结。
-
*to++ = *from++
这行代码的执行顺序是什么?
根据C语言的预算符的优先级,且++在符号后面,有下面的结果:
* > = > ++
所以上面的一行代码可以翻译成下面的代码段:
*to = *from; to++; from++;
这样我们通过函数的参数列表和这里的运算猜测,不难看出,这里大概是利用指针对两个数组进行的赋值操作,然后指向各自数组的指针各自后移指向下一个单元。
-
switch-case
语句没有break也没有defult程序中没有break部分意味着会将程序入口以及之后的case语句后面的执行部分全部执行一遍。也就是从case某个数之后一直进行赋值运算,将from数组的元素赋值到to数组中去。
-
赋值几次?
解决这个问题,实在是没看出来哪里是函数的入口,所以根据上面的分析结果结合查找的一些资料对函数进行了一次复现:
#include<stdio.h> int count(int* to, int* from, int count); int main() { int from[20]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}; int to[20]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; count(to,from,18); for(int i = 0; i < 20; i++ ){ printf("%d ", to[i]); } return 0; } int count(int* to, int* from, int count) { int n = (count + 7) / 8; switch (count % 8) { case 0: do { *to++ = *from++; printf("I am in 'case %d' ",0); case 7: *to++ = *from++; printf("I am in 'case %d' ",7); case 6: *to++ = *from++; printf("I am in 'case %d' ",6); case 5: *to++ = *from++; printf("I am in 'case %d' ",5); case 4: *to++ = *from++; printf("I am in 'case %d' ",4); case 3: *to++ = *from++; printf("I am in 'case %d' ",3); case 2: *to++ = *from++; printf("I am in 'case %d' ",2); case 1: *to++ = *from++; printf("I am in 'case %d' \n",1); } while (--n > 0); } }
代码的执行结果如下:
然后我们将count的值改为20,新的输出结果如下:
-
分析
根据上面的输出我们就能得到有关循环的入口的问题:
当n=18时,18 % 8 = 2;程序的输出从case 2开始
当n=20时,20 % 8 = 4;程序的输出从case 4开始
得到结论:循环的入口与do所在的case 0并没有关系,程序的入口在case的值处。switch的值在哪里,程序循环体的入口就在哪里。
-
综上所述
整个程序的运行逻辑如下:
以count模8 的值为程序的入口,由于没有break和default语句,所以将入口及以下的代码都会执行一遍,又由于这是一个循环结构,所以程序在执行了最后一个case 1 后面的指令之后转跳到case 0 n–,然后继续往下寻找着不存在的break,到最后一句case 1后的程序执行完毕,n–,然后继续转跳到case 0语句……这个过程不断地重复,直到所有n为1为止。由于循环体switch case语句的巧妙排布,和上面代码的准备,这个程序对to和from两个数组的运算刚好进行count次。实现了从form数组赋值count个到to数组的操作。
也就是说,整个程序在做的事情就是:从from数组中复制count个到to数组中。