码迷,mamicode.com
首页 > 其他好文 > 详细

Duff设施,一种奇怪的循环

时间:2015-09-17 21:17:48      阅读:154      评论:0      收藏:0      [点我收藏+]

标签:

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

问这个函数的作用?咋一看,多会觉得这个东西居然能运行。是的,他还是一种特殊的循环,我也对他能通过编译而感到好奇。

先从汇编代码看一下这个函数主要运行流程:

switch (count % 8)
013E5E90    mov eax,dword ptr [count]     ;EAX = count 
013E5E93    and eax,80000007h         ; EAX = EAX % 8 


013E5E98    jns send+3Fh (013E5E9Fh)      ;处理可能出现的负数求模的情况 
013E5E9A    dec eax
013E5E9B    or eax,0FFFFFFF8h
013E5E9E    inc eax


013E5E9F    mov dword ptr [ebp-0D0h],eax
013E5EA5    cmp dword ptr [ebp-0D0h],7    ;if (EAX > 7) 跳过switch 
013E5EAC    ja $LN10+0F3h (013E5FB2h)


013E5EB2    mov ecx,dword ptr [ebp-0D0h]       ;ECX = EAX
013E5EB8    jmp dword ptr [ecx*4+13E5FBCh]  ;跳入对应的switch处理例程

 

//switch这里有很多有意思的内容,但是先把汇编看完。

。。。。中间的那些   *to++ = *from++;  就不看了。。。。

接着:

 while (--n > 0);
013E5F9F    mov eax,dword ptr [n]     ;EAX = N 
013E5FA2   sub eax,1             ;EAX = EAX - 1 
013E5FA5   mov dword ptr [n],eax      ;N = EAX
013E5FA8   cmp dword ptr [n],0      
013E5FAC   jg $LN10 (013E5EBFh)     ; if(N>0)   jmp   013E5EBFh(case 1:的地址)

 

这里循环的013E5EBFh地址我们可以看到就是case 0 例程的地址

case 0:

  do{ *to++ = *from++;


013E5EBF mov eax,dword ptr [to]
013E5EC2 mov ecx,dword ptr [from]
013E5EC5 mov edx,dword ptr [ecx]

也就是说如果循环条件成立程序会跳至case 0 的例程去运行。

 

总结一下这个函数运行方法:、

传入三个参数  to,from是2个数组,count是个int,然后 n = (count +7)%8, n代表了接下来的循环次数。

switch(count % 8) 跳入count与8的模的例程。

由于所有的例程都是 *to++ = *from++;而且没有break;

这整个函数的意义可以用一句话表示: COPY(to,from,count)将from数组中count个数的元素拷贝进to数组。

 

是不是觉得这个答案好无趣,废尽力气写了个没什么用的东西,一般我们都这样写数组拷贝的函数:

void my_send(int *to, int *from, int count)
{
for (int i = 0; i != count; ++i)

{
*to++ = *from++;
}
}

与他相比,这个奇怪的函数它的代码更长,但是由于for循环中每一次都要去判断 i != count,所以相对的这个奇怪的代码运行效率可能会更高一点

(这点我没去实际验证,希望有人去写个计时器算算看,现在计算机那么快估计速度差距1个毫秒都不到吧。。。。)

 

最后,看一个说过的有趣的地方:

为什么是8? 为什么将count去8的模然后分成8份(表达能力欠佳,虽然估计不会有人看,但是大家应该能懂我的意思),按照这个程序的逻辑无论是10还是100都是可以实现的。

我们看一下汇编:

switch (count % 8)
013E5E90    mov eax,dword ptr [count]     ;EAX = count 
013E5E93    and eax,80000007h         ; EAX = EAX % 8 


013E5E98    jns send+3Fh (013E5E9Fh)      ;处理可能出现的负数求模的情况 
013E5E9A    dec eax 
013E5E9B    or eax,0FFFFFFF8h 
013E5E9E    inc eax

将可能出现的负数情况排除,这句汇编很有意思

013E5E93    and eax,80000007h         ; EAX = EAX % 8 

对二进制比较熟悉的话可以发现  一个数对8的模就是将它转换为2进制,然后留下最后面3位

比如 122  二进制   1111010   留下最后3位   010   他对8的模就是 2

如果我们使用其他的数字比如7,那么汇编就没那么简单了(下面是我直接手写的,可能会有问题)

mov   eax, dword ptr [num]    ;eax = num

mox   ebx, 7          ;ebx = 7

idiv    eax,ebx         ;edx = eax %ebx

至少需要3行,还没有考虑会出现的负数问题与push pop的原数据保存

 

 

 

 

 

 

 

。。。。所以这到底有什么用呢。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

Duff设施,一种奇怪的循环

标签:

原文地址:http://www.cnblogs.com/Windogs/p/4817480.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!