如何实现类似printf( const char *format [, argument]... )这种参数不
固定的函数,
 比如你可以这样 printf("hello");  //一个参数 
 也可以printf("%s%s", str1, str2); //三个参数

解决方案 »

  1.   

    用 stdarg.h 中的 va_arg, va_end, va_start 三个宏实现
    范例:
    // crt_va.c
    /* The program below illustrates passing a variable
     * number of arguments using the following macros:
     *      va_start            va_arg              va_end
     *      va_list             va_dcl (UNIX only)
     */#include <stdio.h>
    #define ANSI            /* Comment out for UNIX version     */
    #ifdef ANSI             /* ANSI compatible version          */
    #include <stdarg.h>
    int average( int first, ... );
    #else                   /* UNIX compatible version          */
    #include <varargs.h>
    int average( va_list );
    #endifint main( void )
    {
       /* Call with 3 integers (-1 is used as terminator). */
       printf( "Average is: %d\n", average( 2, 3, 4, -1 ) );   /* Call with 4 integers. */
       printf( "Average is: %d\n", average( 5, 7, 9, 11, -1 ) );   /* Call with just -1 terminator. */
       printf( "Average is: %d\n", average( -1 ) );
    }/* Returns the average of a variable list of integers. */
    #ifdef ANSI             /* ANSI compatible version    */
    int average( int first, ... )
    {
       int count = 0, sum = 0, i = first;
       va_list er;   va_start( er, first );     /* Initialize variable arguments. */
       while( i != -1 )
       {
          sum += i;
          count++;
          i = va_arg( er, int);
       }
       va_end( er );              /* Reset variable arguments.      */
       return( sum ? (sum / count) : 0 );
    }
    #else       /* UNIX compatible version must use old-style definition.  */
    int average( va_alist )
    va_dcl
    {
       int i, count, sum;
       va_list er;   va_start( er );            /* Initialize variable arguments. */
       for( sum = count = 0; (i = va_arg( er, int)) != -1; count++ )
          sum += i;
       va_end( er );              /* Reset variable arguments.      */
       return( sum ? (sum / count) : 0 );
    }
    #endif
      

  2.   

    这涉及到调用者平衡堆栈还是执行者平衡堆栈的问题,由于参数可变所以必须是调用者平衡堆栈在函数声明前加上__cdecl关键字由函数定义可见,printf的参数个数是可变的,函数内部无法预先知道调用者压入的参数个数,函数只能通过分析第一个参数字符串的格式来获得压入参数的信息,由于这里参数的个数是动态的,所以必须由调用者来平衡堆栈,这里便使用了__cdecl调用规则。BTW,Windows系统的API函数基本上是__stdcall调用形式,只有一个API例外,那就是wsprintf,它使用__cdecl调用规则,同printf函数一样,这是由于它的参数个数是可变的缘故。
    看看这里的代码:#pragma comment(linker,"/entry:main") //定义程序的入口
    #include <windows.h>_CRTIMP int (__cdecl *printf)(const char *, ...); //定义STL函数printf
    /*---------------------------------------------------------------------------
    printf函数是C语言的标准函数库中函数,VC的标准函数库由msvcrt.dll模块实现。
    ---------------------------------------------------------------------------*/
    void main()
    {
    int var;
    HMODULE hMsvcrt=LoadLibrary("msvcrt.dll");
    printf=(void *)GetProcAddress(hMsvcrt,"printf");
    printf("0x%08x\n",&var);
    }
      

  3.   

    再补充一下可变参数函数是如何调用它的参数的。可变参数的左边第一个参数一定要包含参数个数总数和长度的信息,然后根据第一个参数的地址加上偏移量来访问后面的参数int (__cdecl *test)(int param1, ...)
    {
    int param2=*(&param1+1);//param2=5678;
    }
    //--------------------------------
    test(1234,5678);
    //----------------├———————┤<—函数执行时的栈顶(ESP)、低端内存区域
    │    ……      │
    ├———————┤
    │    var 1     │
    ├———————┤
    │    var 2     │
    ├———————┤
    │    var 3     │
    ├———————┤
    │     RET      │
    ├———————┤<—“__cdecl”函数返回后的栈顶(ESP)
    │ parameter 1  │
    ├———————┤
    │ parameter 2  │
    ├———————┤
    │ parameter 3  │
    ├———————┤<—“__stdcall”函数返回后的栈顶(ESP)
    │    ……      │
    ├———————┤<—栈底(基地址 EBP)、高端内存区域