zoukankan      html  css  js  c++  java
  • 可变参数函数设计


    #include "stdafx.h"
    #include 
    <stdio.h>
    #include 
    <stdarg.h>

    int mul(int num,int data1,)
    {
        
    int total = data1;
        
    int arg,i;
        va_list ap;
        va_start(ap,data1);
        
    for(i=1;i<num;i++)
        {
            arg 
    = va_arg(ap,int);
            total
    *=arg;
        }
        va_end(ap);
        
    return total;
    }

    long mul2(int i,)
    {
        
    int *p,j;
        p 
    = &i+1;//p指向参数列表下一个位置
        long s = *p;
        
    for (j=1;j<i;j++)
            s 
    *= p[j];
        
    return s;
    }

    int main()
    {
        printf(
    "%d\n",mul(3,2,3,5));
        printf(
    "%d\n",mul2(3,2,3,5));
        
    return 0;
    }



    printf的设计
    #include "stdio.h"
    #include 
    "stdlib.h"
    #include 
    <stdarg.h>

    void myprintf(char* fmt, )        //一个简单的类似于printf的实现,//参数必须都是int 类型
    {
        
    //char* pArg=NULL;               //等价于原来的va_list
        va_list pArg;
        
    char c;
       
       
    // pArg = (char*) &fmt;          //注意不要写成p = fmt !!因为这里要对参数取址,而不是取值
      
    // pArg += sizeof(fmt);         //等价于原来的va_start         
        va_start(pArg,fmt);

        
    do
        {
            c 
    =*fmt;
            
    if (c != '%')
            {
                putchar(c);            
    //照原样输出字符
            }
            
    else
            {
    //按格式字符输出数据
                switch(*++fmt)
                {
                
    case 'd':
                    printf(
    "%d",*((int*)pArg));           
                    
    break;
                
    case 'x':
                    printf(
    "%#x",*((int*)pArg));
                    
    break;
                
    case 'f':
                    printf(
    "%f",*((float*)pArg));
                
    default:
                    
    break;
                }
                
    //pArg += sizeof(int);               //等价于原来的va_arg
                va_arg(pArg,int);
            }
            
    ++fmt;
        }
    while (*fmt != '\0');
        
    //pArg = NULL;                               //等价于va_end
        va_end(pArg);
        
    return;
    }
    int main(int argc, char* argv[])
    {
        
    int i = 1234;
        
    int j = 5678;
       
        myprintf(
    "the first test:i=%d",i,j);
        myprintf(
    "the secend test:i=%f; %x;j=%d;",i,0xabcd,j);
        system(
    "pause");
        
    return 0;
    }


    可变参数在编译器中的处理 

        我们知道va_start,va_arg,va_end是在stdarg.h中被定义成宏的, 由于1)硬件平台的不同 2)编译器的不同,所以定义的宏也有所不同,下面以VC++stdarg.hx86平台的宏定义摘录如下(’"’号表示折行):
    typedef char * va_list; 
    #define _INTSIZEOF(n) \ 
    ((
    sizeof(n)+sizeof(int)-1)&~(sizeof(int- 1) ) 
    #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) 
    #define va_arg(ap,t) \ 
    *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) 
    #define va_end(ap) ( ap = (va_list)0 ) 

        定义_INTSIZEOF(n)主要是为了某些需要内存的对齐的系统.C语言的函数是从右向左压入堆栈的,(1)是函数的参数在堆栈中的分布位置.我们看到va_list被定义成char*,有一些平台或操作系统定义为void*.再看va_start的定义,定义为&v+_INTSIZEOF(v),&v是固定参数在堆栈的地址,所以我们运行va_start(ap, v)以后,ap指向第一个可变参数在堆栈的地址,如图:

    高地址|-----------------------------| 
    |函数返回地址 | 
    |-----------------------------| 
    || 
    |-----------------------------| 
    |第n个参数(第一个可变参数) | 
    |-----------------------------|<--va_start后ap指向 
    |第n-1个参数(最后一个固定参数)| 
    低地址
    |-----------------------------|<-- &
    图( 
    1 ) 

        然后,我们用va_arg()取得类型t的可变参数值,以上例为int型为例,我们看一下va_argint型的返回值: j= ( *(int*)((ap += _INTSIZEOF(int))-_INTSIZEOF(int)) );
    首先ap+=sizeof(int),已经指向下一个参数的地址了.然后返回ap-sizeof(int)int*指针,这正是第一个可变参数在堆栈里的地址(2).然后用*取得这个地址的内容(参数值)赋给j.

    高地址|-----------------------------| 
    |函数返回地址 | 
    |-----------------------------| 
    || 
    |-----------------------------|<--va_arg后ap指向 
    |第n个参数(第一个可变参数) | 
    |-----------------------------|<--va_start后ap指向 
    |第n-1个参数(最后一个固定参数)| 
    低地址
    |-----------------------------|<-- &
    图( 
    2 ) 

        最后要说的是va_end宏的意思,x86平台定义为ap=(char*)0;使ap不再指向堆栈,而是跟NULL一样.有些直接定义为((void*)0),这样编译器不会为va_end产生代码,例如gcclinuxx86平台就是这样定义的.在这里大家要注意一个问题:由于参数的地址用于va_start,所以参数不能声明为寄存器变量或作为函数或数组类型.关于va_start, va_arg, va_end的描述就是这些了,我们要注意的是不同的操作系统和硬件平台的定义有些不同,但原理却是相似的

  • 相关阅读:
    traceroute命令
    ifconfig命令
    netstat命令
    ps命令
    Vue3.0新特性
    Shadow DOM的理解
    解决ufw下pptp客户端连接问题
    Event对象
    java面试一日一题:讲下mysql中的索引
    java面试一日一题:讲下mysql中的redo log
  • 原文地址:https://www.cnblogs.com/flying_bat/p/881986.html
Copyright © 2011-2022 走看看