c 中可变参数的实现

Posted Young_For_You

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c 中可变参数的实现相关的知识,希望对你有一定的参考价值。

我们在C语言编程中有时会遇到一些参数个数可变的函数,例如printf()函数,其函数原型为:  
   例一: int   printf(   const   char*   format,   ...);
它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的(用三个点“…”做参数占位符),实际调用时可以有以下的形式:  

  printf("%d",i);    
    printf("%s",s); 
    printf("the   number   is   %d   ,string   is:%s",   i,   s);

例二:vfprintf

 /*
  vfprintf 函数的原型
  int vfprintf ( FILE * stream, const char * format, va_list arg ); 
 */

void test(char* ch, ...)
{
    va_list arg;
    va_start(arg, ch);   //初始化 arg
    vfprintf(stdout, ch, arg);   
    va_end(arg);
}

void main()
{

    test("%s,%d\\n", "test1", 100);
    test("%s,%s,%d\\n", "test1", "test2", 100);
}

 

 

C语言中可变参数函数实现原理:


C函数调用的栈结构

 可变参数函数的实现与函数调用的栈结构密切相关,正常情况下C的函数参数入栈规则:参数是从右到左,逐一压入栈中的(栈的延伸方向是从高地址到低地址,栈底的占领着最高内存地址,先入栈的参数,其地理位置也就最高)

void fun1(char a, int b, double c, short d) ;

int main() 
{
    fun1(17, 5.40, "hello world");
    return 0;
}
a = 0x0022FF50
b = 0x0022FF54
c = 0x0022FF5C

因此,函数的所有参数是存储在线性连续的栈空间中的,基于这种存储结构,这样就可以从可变参数函数中必须有的第一个普通参数来寻址后续的所有可变参数的类型及其值。

我们基本可以得出这样一个结论:

 c.addr = b.addr + x_sizeof(b);  /*注意:  x_sizeof !=sizeof */
 b.addr = a.addr + x_sizeof(a);

由于内存对齐,编译器在栈上压入参数时,不是一个紧挨着另一个的,编译器会根据变参的类型将其放到满足类型对齐的地址上的,这样栈上参数之间实际上可能会是有空隙的。所以此时的ap计算应该改为:ap =  (char *)ap +sizeof(int) + _INTSIZEOF (n);

 

 

为了满足代码的可移植性,C标准库在stdarg.h中提供了诸多便利以供实现变长长度参数时使用:

#define a_list char*        
#define _INTSIZEOF (n) ((sizeof(n)+ sizeof(int )-1) & ~(sizeof(int )-1))            //将 n 与 4 向上对齐
#define va_start (ap,v) (ap = (va_list)&v + _INTSIZEOF(v))      //宏初始化变量ap
#define va_arg (ap,t) (*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))     //va_arg返回可变的参数
#define va_end (ap) (ap = (va_list)0)      //用va_end宏结束可变参数的获取
 
//一个可变参数应用的简单例子
//可变参数
int Avg(int n, ...)
{
      a_list arg;  //char *arg;
      va_start(arg, n );//init
      int sum = 0;
      for (int i = 0; i<n; ++i)
      {
          sum += va_arg(arg,int );
      }
      va_end(arg);  //arg = 0; //野指针
      return sum / n ;
}
void main() { int avg = Avg(5, 10, 20, 30,20,90); printf( "avg = %d\\n", avg); }


 

欢迎讨论~~

 
 
 

以上是关于c 中可变参数的实现的主要内容,如果未能解决你的问题,请参考以下文章

C语言可变参数

c 中可变参数的实现

亲密接触C可变参数函数

C语言怎么实现可变参数?

揭密X86架构C可变参数函数实现原理

揭密X86架构C可变参数函数实现原理