Duff设施,一种奇怪的循环

看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的原数据保存

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

时间: 2024-10-10 02:06:12

Duff设施,一种奇怪的循环的相关文章

java的break,另一种用法(多层循环嵌套)

break的另一种用法: 1.跳出外循环 outer:for(int j=0;i<4;j++){//outer随便定义的一个标签 for(int i=0;i<10;i++){ if(i==6) break outer; System.out.println("i="+i); } System.out.println("j="+j); } 2.跳出内循环 for(int j=0;i<4;j++){ inner:for(int i=0;i<10;

while 语句的三种控制/结束循环方式

while语句若一直满足条件,则会不断的重复下去.但有时,我们需要停止循环,则可以用下面的三种方式: 1.在while语句中设定条件语句,条件不满足,则循环自动停止:ie: 只输出3的倍数的循环:范围:0到20. current_number = 0 while current_number < 20: current_number += 1 if current_number % 3 != 0: continue print(current_number) 敲黑板,敲黑板,重点在这里: 先将起

一种实现无缝循环播放音乐方案

场景: 为了节省页面资源,往往需要将一段小音频循环播放,通常做法是在audio标签上添加loop属性,但不幸的是,该属性并不能保证无缝循环(gapless looping)播放,明显的感觉到中间的停顿. 解决方案: 使用audio标签的Web API提供的方法和属性进行循环播放,具体如下 事件名称 事件作用 timeupdate 当前播放的时长发生改变时触发     属性名称 属性作用 currentTime                 用来获取或控制当前播放的时间,单位为s duratio

sql 循环语句几种方式(变量循环,游标循环,事务)

--第一 declare @orderNum varchar(255) create table #ttableName(id int identity(1,1),Orders varchar(255)) declare @n int,@rows int insert #ttableName(orders) select orderNum from FOrders where orderId<50 --select @rows=count(1) from pe_Orders select @ro

Scala 有几种其它的循环结构

Scala while 循环 和许多语言类似,while 循环在条件为真的时候会持续执行一段代码块.例如,下面的代码在下一个星期五,同时又是13号之前,每天打印一句抱怨的话: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // code-examples/Rounding/while-script.scala  // WARNING: This script runs for a LOOOONG time!  import java.util.Calendar

leetcode206 反转链表 两种做法(循环,递归)

反转链表 leetcode206 方法1 循环 ··· public ListNode reverseList(ListNode head) { if (head == null || head.next == null) { return head; } ListNode now = head; while (now.next != null) { ListNode temp = now.next; now.next = now.next.next; temp.next = head; hea

一种奇怪的念头

https://detail.tmall.com/item.htm?spm=a220m.1000858.1000725.41.uUmVrA&id=531159310471&skuId=3166266342650&user_id=2103509739&cat_id=50025135&is_b=1&rn=fd2d47106c33fa31df930907ce3c1bdd https://detail.tmall.com/item.htm?spm=a220m.100

三种另外的循环 while{} 和do{}while{}还有switch case

while的写法 var i=0; while(i<5){ document.write("12378<br />");  i++;} while(true)----死循环的写法.{ } do{}while();不管你正确与否,都先去执行一次,然后去判断,若不满足,则不继续执行var a =1;do{ document.write("12346789");-------不管下面的条件是什么,都会先打印出来”123456789“}while(a<

Java中几种常见的循环

多重if_else: package com.dengchaoqun.ht; public class Double_For02 { /** * * 打印乘法表 */ public static void main(String[] args) { for (int i = 1; i < 10; i++) { for (int j = 1; j <= i; j++) { int a = i * j; System.out.print(i + "*" + j + "