函数原型: int printf(const char *format[,argument]...)

       返 回 值: 成功则返回实际输出的字符数,失败返回-1.

 函数说明:

       在printf()函数中,format后面的参数个数不确定,且类型也不确定,这些参数都存放在栈内.调用printf()函数时,根据format里的格式("%d %f...")依次将栈里参数取出.而取出动作要用到va_arg、va_end、va_start这三个宏定义,再加上va_list.

     (1)va_list事实上是一char *类型,即:

            typedef char* va_list;

     (2)三个宏定义:

            #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,type)  ( *(type *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) 

            #define va_end(ap)          ( ap = (va_list)0 ) 

   【attentionC语言中可变参数的原理---printf()函数 

            int printf(const char* format,...);   

      使用过C语言的人所再熟悉不过的printf函数原型,它的参数中就有固定参数format和可变参数(用”…”表示).而程序员又可以用各种方式来调用printf,如: 

            printf("%d ",value);   

            printf("%s ",str);

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

       可以看出,该函数的参数格式不固定,参数类型不固定.在C语言中使用宏来处理这些可变参数.这些宏看起来很复杂,其实原理挺简单,即根据参数入栈的特点从最靠近第一个可变参数的固定参数开始,依次获取每个可变参数的地址.

 (1)宏va_start

      通过该宏定义可以获取到可变参数表的首地址,并将该地址赋给指针ap.

 (2)宏va_arg

      通过该宏定义可以获取当前ap所指向的可变参数,并将指针ap指向下一个可变参数.注意,该宏的第二个参数为类型.

 (3)宏va_end

      通过该宏定义可以结束可变参数的获取.

程序员通过这三个宏定义就可以实现对可变参数的处理.例如:

#include <stdio.h>  

  

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,type)  ( *(type *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )   

   #define va_end(ap)       ( ap = (va_list)0 )   

  

int cal_val(int c, ...)   

{   

    int sum = c;   

    va_list ap;              //声明指向char型的指针  

    va_start(ap,c);          //获取可变参数列表的首地址,并赋给指针ap  

  

    c = va_arg(ap,int);      //从可变参数列表中获取到第一个参数(返回值即为参数)  

    while(0 != c)   

    {   

        sum += c;   

        c = va_arg(ap,int);  //循环的从可变参数列表中获取到参数(返回值即为参数)  

    }  

    va_end(ap);              //结束从可变参数列表中获取参数  

    return sum;   

}    

   

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

{   

    int value1;  

      

    value1 = cal_val(1,2,3,4,5,6,7,8,9,0);   

    printf("value1=%d/n",value1);  

    value2 = cal_val(6,7,8,9,0);   

    printf("value2=%d/n",value2);  

      

    return 0;   

}

【C/C++开发】C语言实现函数可变参数的更多相关文章

  1. C语言中函数可变参数解析

    大多数时候,函数中形式参数的数目通常是确定的,在调用时要依次给出与形式参数对应的所有实际参数.但在某些情况下希望函数的参数个数可以根据需要确定.典型的例子有 大家熟悉的函数printf().scanf ...

  2. C语言之函数可变参数

    先上一段代码: #include<cstdarg> #include<iostream> #include<string> using namespace std; ...

  3. C语言函数可变参数列表

    C语言允许使用可变参数列表,我们常用的printf函数即为可变参数函数,C标准库提供了stdarg.h为我们提供了这方面支持:该头文件提供了一些类型和宏来支持可变参数列表,包括类型va_list,宏v ...

  4. C 函数可变参数

    C 函数可变参数 C 语言中用 ... 表示可变参数,例如: void fun(int x ...) 头文件 cstdarg.h 中包含可变参数类型va_list和处理可变参数的三个宏: va_lis ...

  5. C语言中的可变参数-printf的实现原理

    C语言中的可变参数-printf的实现原理 在C/C++中,对函数参数的扫描是从后向前的.C/C++的函数参数是通过压入堆栈的方式来给函数传参数的(堆栈是一种先进后出的数据结构),最先压入的参数最后出 ...

  6. c#编程基础之函数可变参数

    可变参数:int sum (params int[] values)int sum (string name,params int[] values) 注意:params参数必须是形参表中的最后一个参 ...

  7. PHP函数可变参数列表的具体实现方法介绍

    PHP函数可变参数列表可以通过_get_args().func_num_args().func_get_arg()这三个函数来实现.我们下面就对此做了详细的介绍. AD:2014WOT全球软件技术峰会 ...

  8. Python函数可变参数*args及**kwargs详解

    初学Python的同学们看到代码中类似func(*args, **kwargs)这样的函数参数定义时,经常感到一头雾水. 下面通过一个简单的例子来详细解释下Python函数可变参数*args及**kw ...

  9. C语言中的可变参数函数的浅析(以Arm 程序中的printf()函数实现为例) .

    我们在C语言编程中会遇到一些参数个数可变的函数,一般人对它的实现不理解.例如Printf(): Printf()函数是C语言中非常常用的一个典型的变参数函数,它 的原型为: int printf( c ...

随机推荐

  1. 利用Synplify Pro 加时钟约束的问题

    可以改名称为"design.ucf"加成新约束. 在使用Xilinx ISE进行综合时,可以与Synplify Pro软件配合,实现较高的综合性能.但是,有时会出现如下问题: “E ...

  2. Maven 异常 druid jar冲突

    异常: 十二月 25, 2017 11:04:41 下午 org.apache.tomcat.util.digester.SetPropertiesRule begin 警告: [SetPropert ...

  3. 线程池(6)-submit与execute区别

    在线程池里提交任务经常见到submit与execute,如何选择,傻傻分不清楚.那么他们俩有什么区别,使用场景是什么?这篇博客将会介绍. 1.方法定义 void execute(Runnable co ...

  4. Java IO管道流

    import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; publi ...

  5. 44个Java性能优化

    44个Java性能优化 首先,代码优化的目标是: 减小代码的体积 提高代码运行效率 代码优化细节 1 .尽量指定类.方法的final修饰符 ​ 带有final修饰符的类是不可派生的.在Java核心AP ...

  6. vuejs2从入门到精通视频教程

    https://www.cnblogs.com/web-666/p/8648607.html 一.基础部分 0.课件 1.介绍 2.vue实例 3.模板语法 4.计算属性和观察者 5.Class与St ...

  7. 解决request.getSession().getServletContext().getRealPath("/")为null问题

    今天把程序部署到服务器,发现异常,FileNotFound异常,很快定位到getServletContext().getRealPath("/");返回空的问题.这个问题通常是传递 ...

  8. bat和cmd文件是什么,dos又是什么东西

    bat文件是dos下的批处理文件.批处理文件是无格式的文本文件,它包含一条或多条命令.它的文件扩展名为 .bat 或 .cmd.在命令提示下键入批处理文件的名称,或者双击该批处理文件,系统就会调用cm ...

  9. transition 滑动动画

    html: <!-- 组件会在 `currentTabComponent` 改变时改变 --> <transition name="slide" mode=&qu ...

  10. Android输入法遮挡了输入框,使用android:fitsSystemWindows="true"后界面顶部出现白条解决方案

    我的最外层是LinearLayout,自定义CustomLinearLayout继承LinearLayout,重写fitSystemWindows和onApplyWindowInsets两个方法: p ...