zoukankan      html  css  js  c++  java
  • 王爽-汇编语言-综合研究五-函数接收不定量参数

    (一) 研究目的

    我们知道,在C语言中,函数是可以传递参数的。有些函数在声明是就定义了要传的参数的个数,比如我们定义void a(int i);这说明函数a只接受一个int型参数。而有些函数,比如print函数,是可以接收不定个数的参数的。那函数是怎样接收不定量参数的呢?

    (二) 研究过程

    1) 有限个数的参数

    首先我们来看程序是如何传参数的。我们编写一个程序,让他传递有限个参数:

    clip_image001

    我们编译链接,然后反汇编查看其代码:

    clip_image003clip_image005

    我们看其代码,首先,在main函数中,分别将‘a’与2对应的ASCLL码放到了Al中,然后PUSH AX入栈,在这里,放入,入栈这个过程进行了两次,入栈的数据就是我们的参数。然后调用子函数。子函数中。第一条语句是PUSH BP,这条语句很重要。然后我们看,MOV AL,[BP+04]。那么放到AL中的是什么数据呢?

    我们分析:假设程序一开始ss:sp指向的是0005这个位置,那么,我们从开始到现在入栈三次后,栈内的空间如下分布。而BP的默认段地址寄存器也是SS,在子程序的开始SP的值给了BP。那么也就是说SS:BP指向的也是现在SS:SP指向的位置。那么,向AL中放入的数据是不是就是第一次PUSH的数据,也就是我们的第一个参数。

    clip_image006

    我们单步验证:

    clip_image008clip_image010

    我们看到,两次向AL中移动的就是我们传入的参数。由此我们可以知道,在C语言中参数的传递是通过栈来进行的。在函数调用前,将参数放入AX中,AX入栈,进入调用函数后,先把栈中的值出栈到AX中。这样就完成了函数间参数值的传递工作。

    而且我们还发现这样两个现象:首先,在参数入栈的时候,程序首先入栈的是后面的参数,即入栈的时候参数是倒序入栈,这是由于栈的特性,出栈时就变成正序出栈。第二,在程序中,并没有出现类似参数个数的值。

    2) 不定参数个数的函数

    我们编写一个不定参数个数的函数如下:

    clip_image012

    我们编译连接之后反汇编如下:

    clip_image014clip_image016clip_image018clip_image020clip_image022

    3) 研究printf函数

    上一个程序,我们给函数参数中传递了参数的个数,在程序中通过这个数来控制对参数的操作。但是printf的格式中,并没有传递类似的变量。那么printf是通过什么来得知和控制变量个数的呢。

    我们写一个printf函数:

    clip_image023

    反汇编如下:

    clip_image025

    这是main函数。我们看到这里调用了0AC5这个子程序。我们分析,这是在做完准备工作后进入了printf函数的子程序。而且,我们看到,“%c%d”这个参数,放入栈中的时候,被翻译成了0194。

    我们知道,函数中,两个参数之间用逗号分隔,也就是说“%c%d”应该是按照一个参数传递进去的。也就是说,它应该是被认为是一个字符串。“%c%d”可能不是很清晰。我们编写一个更多参数的peintf函数。

    clip_image026

    其代码如下:

    clip_image028

    这里传入的第一个参数值仍然为0194。这个值有可能是字符串存放的地址。我们查看:

    clip_image030

    我们看到这里存放的果然是这个字符串。

    也就是说,我们完全可以借鉴这种机制,看字符串的长度,来确定参数的个数。

    4) 我们自己的printf函数

    首先我们要确定我们的函数传递参数的方式,我们要传递无个数限制的参数的,而且我们是通过第一个字符串来确定我们参数的个数的。我们根据以上的研究结果定义一个函数如下:

    void print(char * str,...);

    main()

    {

    print("%c %c %d %d",'a','b','c','e');

    }

    void print(char * str,...)

    {

    int i=0;

    int j=0;

    int sj=0;

    int js=0;

    int dx[200];

    int color=2;

    char ch=str[i++];

    int wz=2;

    while(ch)

    {

    if(ch=='%')

    {

    ch=str[i++];

    if(ch=='c')

    {

    *(char far *)(0xb8000000+wz)=*(int *)(_BP+6+j);

    *(char far *)(0xb8000001+wz)=color;

    wz=wz+2;

    j++;

    }

    if(ch=='d')

    {

    js=0;

    sj=*(int*)(_BP+6+j);

    j++;

    if(sj==0)

    {

    *(char far*)(0xb8000000+wz)='0';

    *(char far*)(0xb8000001+wz)=color;

    wz=wz+2;

    }

    else

    {

    while(sj!=0)

    {

    dx[js]=sj%10;

    sj=sj/10;

    js++;

    }

    js--;

    for(;js>=0;js--)

    {

    *(char far *)(0xb8000000+wz)=dx[js]+48;

    *(char far *)(0xb8000001+wz)=color;

    wz=wz+2;

    }

    }

    }

    j++;

    }

    else

    {

    *(char far *)(0xb8000000+wz)=ch;

    *(char far *)(0xb8000001+wz)=color;

    wz=wz+2;

    }

    ch=str[i++];

    }

    }

    我们看到运行得结果如下

    clip_image031

    这样,我们就写出了一个简单的printf函数。

  • 相关阅读:
    C#导出数据—使用Word模板书签的使用
    C#动态调用泛型类、泛型方法
    C#中运算符的介绍和使用
    C#中自定义类型转换
    Linux删除文件后没有释放空间
    Linux下用dd命令测试硬盘的读写速度
    强制关闭或重启Linux系统的几种方法
    前端使用a标签启动本地.exe程序
    Linux 命令别名,让alias永久生效
    Linux 自动删除N小时或分钟前的文件
  • 原文地址:https://www.cnblogs.com/shandianlongxiao/p/4025471.html
Copyright © 2011-2022 走看看