文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>quine程序(理解函数调用和参数传递)

quine程序(理解函数调用和参数传递)

时间:2006-10-23  来源:朝花夕拾

下面这个程序的输出为该程序本身
main(a){
         printf(a,34,a="main(a){ printf(a,34,a=%c%s%c,34);}",34);
}

咋一看这个程序有语法错误,事实上没有,除了编译会有warning外,(34为双引号“的ascii码)。
main函数没有接收任何参数,a其实是个占位符号,就好像加载函数给main函数传递了参数a(我把它称为伪参数吧)。
先看下面的这个程序:
main(){
        tt();
}
tt(a){
        a=44;
        printf("%d",a);
}
输出:44
子函数tt在被调用的时候没有接收任何参数,该程序转成汇编代码(gcc -S):
────a.s──────
...
main:
        pushl   %ebp                保存栈帧
        movl    %esp, %ebp          初始化栈指针
        subl    $8, %esp            下面8行(到a0)把esp减40,留下一些空闲的栈空间
        andl    $-16, %esp
        movl    $0, %eax
        addl    $15, %eax
        addl    $15, %eax
        shrl    $4, %eax
        sall    $4, %eax
        subl    %eax, %esp          a0
        call    tt                  调用tt子函数
        leave                       main函数返回前的准备,即movl %ebp,%esp和popl %ebp
        ret
        .size   main, .-main
        .section        .rodata
.LC0:
        .string "%d\n"
        .text
.globl tt
        .type   tt, @function
tt:
        pushl   %ebp            保存栈帧
        movl    %esp, %ebp      初始化栈指针
        subl    $8, %esp
        movl    $44, 8(%ebp)    把44附值给a   
        subl    $8, %esp
        pushl   8(%ebp)         把a压栈,作为调用printf的第二个参数
        pushl   $.LC0           把格式化字符串"%d\n"的地址压栈,作为调用printf的第一个参数
        call    printf          调用printf函数
        addl    $16, %esp
        leave
        ret                     子函数tt返回
...

再来比较一下下面的代码
main(){
        ttt(44);
}
ttt(int a){
        printf("%d",a);
}

子函数ttt接收了一个整型值,汇编代码如下
──────b.s────────
...
main:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        andl    $-16, %esp
        movl    $0, %eax
        addl    $15, %eax
        addl    $15, %eax
        shrl    $4, %eax
        sall    $4, %eax
        subl    %eax, %esp         到这行为止都与上前面的例子一样
        subl    $12, %esp
        pushl   $44                把44压栈,作为子函数ttt的参数
        call    ttt
        addl    $16, %esp
        leave
        ret
        .size   main, .-main
        .section        .rodata
.LC0:
        .string "%d"
        .text
.globl ttt
        .type   ttt, @function
ttt:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        subl    $8, %esp
        pushl   8(%ebp)        把main传来的参数a的值(44)压栈,作为调用printf的第二个参数
        pushl   $.LC0          把格式化字符串 "%d"的地址压栈,作为调用printf的第一个参数
        call    printf
        addl    $16, %esp
        leave
        ret
...

分析两段代码可以发现,它们之间只有微妙的差别(彩色部分),两个程序的栈空间图如下:
500)this.width=500;" border="0">
500)this.width=500;" border="0">
可以给多个伪参数,如ttt(a,b,c),每个这种参数占一个机器字,这里4字节,可以使用的伪参数个数要看编译器预留的栈空间大小了,上面调用ttt的main函数预留了40字节,即10个字,也就是说最多可以有10个这种参数。

理解了这两个程序,那么那个quine也很容易理解了。了解相关内容可以看computer system -a progamming's perspective,中文版为“深入理解计算机系统”。
相关阅读 更多 +
排行榜 更多 +
狂热战车

狂热战车

飞行射击 下载
士兵和小鸡

士兵和小鸡

飞行射击 下载
西部枪射击目标

西部枪射击目标

飞行射击 下载