zoukankan      html  css  js  c++  java
  • 研究不定数量参数的函数并实现一个printf函数

    一、前提知识

    1、如何传递参数(主函数)

    a、函数的参数是通过栈传递,而且是从右到左依次入栈

    b、即使是char型变量,在传递参数时,也是占用两个字节,因为push操作是两个字节为单位的。

    c、showchar('a',2)这样的传入两个常数,也会在堆栈中开辟两个空间,也即对应两个实参变量。

    2、函数如何接收参数(子函数)

    a、 函数接受形参是通过从栈中取的

    b、通过BP可以找到传入参数的值,BP+4是第一个参数,BP+6是第二个参数......取参数是从左到右取的

    3、如何释放参数(主函数)

       释放参数可以通过多次pop来实现。有时是通过“add sp,+数值”来实现的。

    二、研究一个简单的不定数量参数的函数

      测试代码 

    void showchar(int,char,...);
    
    main()
    {
        showchar(8,2,'a','b','c','d','e','f','g','h');
    }
    
    void showchar(int n,char color,...)
    {
        int i;
        char r;
        for (i = 0; i!=n; i++)
        {
            r = *(char *)(_BP+8+i+i);
            r = color;
        }
    }
    View Code

     1、main函数  

    main()
    {
        showchar(8,2,'a','b','c','d','e','f','g','h');
    }

    对应的反汇编代码

    2、showchar函数 

    void showchar(int n,char color,...)
    {
        int i;
        char r;
        for (i = 0; i!=n; i++)
        {
            r = *(char *)(_BP+8+i+i);
            r = color;
        }
    }

       对应的反汇编代码

    3、分析

      函数通过参数一来控制显示字符的循环次数,通过这种方式来接收多个参数。

    三、printf函数确定不定参数个数的方法

      通过第一个参数所指向的字符串中%个数来确定不定参数的个数。

    四、实现一个printf函数

    1、包含printf函数、测试函数的C程序 

    extern void showenter(void);                 /* 在模块showchar.obj中定义 */
    extern void showchar(char);                    /* 在模块showchar.obj中定义 */
                                                /* 在光标位置显示字符,同时光标后移 */
    static void printf(const char * str,...);   /* 使用自己定义的printf函数  */
    static void showint(int num);                
    
    main()
    {
        printf("
    hello %c%c%cld!
    ",'w','o','r');
        printf("
    %c %d %c %d
    ",'l',6553,'o',123);
    }
    
    /***************************************************************************
    函数功能:支持%c、%d功能的printf函数
    输入参数:str以及不定参数
    前提知识:无论char型,还是int型,在传递参数时,都是入栈操作,而且都是以两个
              字节入栈的。因为push指令只能以两个字节来操作。
    实现思路:通过%来统计获取不定参数的个数,参数从堆栈中去获取           
    ****************************************************************************/
    static void printf(const char * str,...)
    {
        char strnum = 0;                                        /* 记录读出字符串str的位置 */
        char paraaddr = 0;                                        /* 记录读出不定参数的位置 */
        
        while(str[strnum]){                                        /* 取出字符为NULL,结束 */
            if(str[strnum] == '%'){                                /* 取出字符为%,看下一个字符 */
                strnum++;                                        /* 读取字符串的位置后移 */
                switch(str[strnum]){                            /* 根据%后边字符的值,选择不同的操作 */
                    case 'c':                                    
                             showchar(*(char*)(_BP+6+paraaddr));/* 为c,则将一个char型大小的参数取出显示 */
                             paraaddr += 2;                        /* 读取不定参数的位置后移 */
                             break;
                    case 'd':
                            showint(*(int*)(_BP+6+paraaddr));    /* 为d,则将一个int型大小的参数取出显示 */
                            paraaddr += 2;                        /* 读取不定参数的位置后移 */
                            break;
                    default:
                            showchar('%');                        /* 不是d,也不是c,就将之前的%显示 */
                            showchar(str[strnum]);                /* 还要把当前字符显示 */
                }
            }
            else                                                /* 取出字符非%,直接显示 */
            {
                if(str[strnum] == '
    ')                            /* 换行符 */
                {
                    showenter();                                /* 用函数showenter显示 */
                }
                else{
                    showchar(str[strnum]);                        /* 其他情况,直接显示 */
                }            
            }
            strnum++;                                            /* 读取字符串的位置后移  */
        }
    }
    /***************************************************************************
    函数功能:显示整型数字
    输入参数:num
    实现思路:先将整型数字从个位依次向高位取数,存在堆栈中。显示的时候,从堆栈的
              高位第一个非零数字开始显示。
    存在问题:函数在VC6.0上能正确运行,但是在TC2.0上不能,比如说不能显示65535
    猜测原因:VC6.0和TC2.0对整型的定义是不同的,前者是4个byte的存储空间,而后者只
              有两个
    ****************************************************************************/
    static void showint(int num)
    {
        char bufstk[5];                        /* 定义栈空间 */
        char p;                                /* 栈顶 */
        
        for(p = 0; p < 5; p++){
            bufstk[p] = num % 10;            /* 从低位到高位依次入栈 */
            num /= 10;
        }
        
        while((p > 0)&&(bufstk[--p] == 0)); /* 舍去高位为0的数字,但不舍弃num=0的个位0 */
        
        do{
            showchar(bufstk[p]+'0');        /* 显示有效数字 */
        }
        while(p--);                            /* 直到栈空 */
    }
    View Code

     2、包含在光标位置显示一个字符和显示换行的汇编程序

     PUBLIC _SHOWCHAR
     PUBLIC _SHOWENTER
    _TEXT SEGMENT BYTE PUBLIC 'CODE'
      ASSUME CS: _TEXT
    ;==================================================================
    ;函数名称:showchar
    ;函数功能:显示一个字符
    ;输入参数:在堆栈中,具体说来是(返回地址+2),目的是为了和C程序无缝对接
    ;==================================================================  
    _SHOWCHAR  PROC NEAR
    
        push ax            ;用到的中间寄存器入栈保存
        push cx
        push bx
        push bp
        
        mov bp,sp        ;模拟C程序的反汇编程序
        mov ah,0eh        ;调用"int 10h"的第0e号功能,显示字符且光标后移
        mov al,[bp+10]    ;字符
        mov bl,07h        ;颜色为黑底白字
        mov bh,0        ;第0页
        mov cx,1        ;重复1次
        int 10h
        
        pop bp
        pop bx
        pop cx
        pop ax
        ret
    
    _SHOWCHAR  ENDP
    
    ;==================================================================
    ;函数名称:showenter
    ;函数功能:显示'
    '
    ;输入参数:无
    ;==================================================================
    _SHOWENTER  PROC NEAR
    
        push bx
        push ax
        push dx
        
        
        mov bh,0    ;页号为0
        mov ah,3     ;取当前光标位置
        int 10h     ;返回参数。DH=行号,DL=列号
        
        inc dh         ;行号加1
        mov dl,0    ;列号为0
        mov ah,2     ;置光标位置
        int 10h     ;入口参数。DH=行号,DL=列号
        
        pop dx
        pop ax
        pop bx
        ret
    
    _SHOWENTER  ENDP
    _TEXT  ENDS
       END
    View Code

    3、编译方法

     
       《汇编语言》326页 研究实验5 “函数如何接收不定数量的参数”
     
  • 相关阅读:
    Http中GET和POST两种请求的区别
    JSON学习笔记
    分页
    python 函数,闭包
    LVS负载均衡中arp_ignore和arp_annonuce参数配置的含义
    return ;
    openssl 在php里
    重装drupal
    protected的意义
    和 和 notepad++
  • 原文地址:https://www.cnblogs.com/amanlikethis/p/3572286.html
Copyright © 2011-2022 走看看