达夫设备程序解析

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);     
        }                           
    }
    

    代码的执行结果如下:

    image-20210322211736727

    然后我们将count的值改为20,新的输出结果如下:

    image-20210322212304739

  • 分析

    根据上面的输出我们就能得到有关循环的入口的问题:

    当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数组中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Blanche117

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值