天天看点

可变参数的函数的原理

原文地址:

可变参数。

1:必须有一个提前参数,(即:...之前必须要有一个参数),用以计算出后面的第一个未知参数的地址. 知道了第一个未知参数的地址之后, 就可以根据fmt格式化串,可以依次计算出剩余的参数地址.

sprintf()的原型:sprintf(char* buffer, const char* fmt, ... ) ,其中,fmt就是提前参数

2:每一个可变阐述函数,其编写者与使用者 都要有一个参数的使用约定。不然,会乱套。

3:可变函数实现的技术基础

1:所有参数,在汇编级别,其大小都是4个字节的整数倍。char,short类型也被扩展成4byte

把任意长度的类型扩展成4byte的整数倍的技术就是 

_intsizeof(n) ( (sizeof(n)+sizeof(int)-1) & ~(sizeof(int)-1))

简化就是,((sizeof(n)+4-1) & ~(4-1))

_intsizeof(char*) = 4

_intsizeof(2byte) = 4

_intsizeof(double)= 8

2:有了上述的知识,下面就是,从可变参数中一个一个去参数了

#define va_start(ap,v) ( ap = (va_list)&v + _intsizeof(v) )

               // :取得已知参数的地址,

               // ap 就是第一个未知参数地址,v就是上述的提前参数fmt

               // eg :fun( int n,...) v就是n的地址。

#define va_arg(ap,t) \

( *(t *)((ap += _intsizeof(t)) - _intsizeof(t)) )  

va_arg()替换后,ap指向t类型后的下一个类型的地址。va_arg本身被替换成了t类型对应参数的值. t类型一般是从fmt格式化串中根据%d,%c等转换的.

比如1: int a = va_art(ap, int);

比如2: fun(int n,...)

{

...

char* pchar = va_arg(ap, char*)

int tmp = va_arg(ap, int)

double dd = va_arg(ap, double)

}

那么,...必然是 char*,int,double的参数顺序!!如果不是这样的顺序,程序将不定期崩溃!!这就

是为什么c不是类型安全的,boost::format好像提供类型安全的格式转换,可以替代sprintf的使用。

对于sprintf()而言,va_arg(av,v)v的值,使用 char* fmt 中提取的。

附:

3)//示例代码2:扩展——自己实现简单的可变参数的函数。(没有用到va_list等宏)

下面是一个简单的printf函数的实现,参考了<the c programming language>中的例子

#include "stdio.h"

#include "stdlib.h"

void myprintf(char* fmt, ...)        //一个简单的类似于printf的实现,//参数必须都是int 类型

    char* parg=null;               //等价于原来的va_list 

    char c;

    parg = (char*) &fmt;          //注意不要写成p = fmt !!因为这里要对//参数取址,而不是取值

    parg += sizeof(fmt);         //等价于原来的va_start          

    do

    {

        c =*fmt;

        if (c != '%')

        {

            putchar(c);            //照原样输出字符

        }

        else

           //按格式字符输出数据

           switch(*++fmt) 

           {

            case'd':

                printf("%d",*((int*)parg));           

                break;

            case'x':

                printf("%#x",*((int*)parg));

            default:

            } 

            parg += sizeof(int);               //等价于原来的va_arg

        ++fmt;

    }while (*fmt != '/0'); 

    parg = null;                               //等价于va_end

    return; 

int main(int argc, char* argv[])

    int i = 1234;

    int j = 5678;

    myprintf("the first test:i=%d/n",i,j); 

    myprintf("the secend test:i=%d; %x;j=%d;/n",i,0xabcd,j); 

    system("pause");

    return 0;

继续阅读