zoukankan      html  css  js  c++  java
  • 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
    ", "test1", 100);
        test("%s,%s,%d
    ", "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 ", avg); }


    欢迎讨论~~

     
     
     
  • 相关阅读:
    centos7 下载并安装.netcore SKD,运行.netcore 应用程序
    小网站到大网站架构的演化之路 学习总结
    suppersocke,websocket 功能学习总结
    定时任务 quartZ
    RabbitMQ 安装和功能点
    rabbitmq 发送 消费消息
    富文本编辑器
    vue AES加密解密
    css动画库
    el-table合并表格
  • 原文地址:https://www.cnblogs.com/shihaochangeworld/p/5563120.html
Copyright © 2011-2022 走看看