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

大家熟悉的函数printf()、scanf()

  可变参数的实现:

  C语言头文件stdarg.h提供了一个数据类型va-list和三个宏(va-start、va-arg和va-end),va—start使vp指向第一个可选参数。va—arg返回参数列表中的当前参数并使vp指向参数列表中的下一个参数。va
 
—end把vp指针清为NULL。函数体内可以多次遍历这些参数,但是都必须以va—start开始,并以va—end结尾。用它们在被调用函数不知道参数个数和类型时对可变参数表进行测试,从而为访问可变参数提供
 
了方便且有效的方法。va-list是一个char类型的指针,当被调用函数使用一个可变参数时,它声明一个类型为va-list的变量,该变量用来指向va-arg和va-end所需信息的位置。
 
下面给出va_list在C中的源码:
    typedef char * va_list; 

  采用ANSI标准形式时,参数个数可变的函数的原型声明是:

     type funcname(type para1, type para2, ...) 
  这种形式至少需要一个普通的形式参数,后面的省略号不表示省略,而是函数原型的一部分,type是函数返回值和形式参数的类型。
 
  调用者在实际调用参数个数可变的函数时,要通过一定的方法指明实际参数的个数,例如把最后一个参数置为空字符串。
  
  下面给出一个具体的例子:
求n个数的和:
int Sum(int n,...)
{
int i = ,sum = ;
va_list vp;
va_start(vp,n); //va—start使vp指向第一个可选参数
for (i=; i<n; i++)
{
sum +=va_arg(vp,int);
}
va_end(vp); //va—end把vp指针清为NULL。
return sum;
}

printf函数实现:(此处只实现了基本类型的打印)

 void my_print(const char *format,...)
{
char c = ;
va_list vp;
va_start(vp,format); //vp指向第一个可选参数
while (*format)
{
c = *format;
switch (c)
{
case '%':
{
char cc = *(++format);
switch (cc)
{
case 'd':
{
char str[];
int n = va_arg(vp,int); //va—arg返回参数列表中的当前参数并使vp指向参数列表中的下一个参数。
char *string = _itoa(n,str,); //把整数转为字符串
print_str(string);
}
break;
case 'f':
{
char str[];
double f = va_arg(vp,double);//va—arg返回参数列表中的当前参数并使vp指向参数列表中的下一个参数
char *string = _gcvt(f,,str);//将浮点型数转换为字符串,取四舍五入
print_str(string);
}
break;
case 'c':
putchar(va_arg(vp,char));
break;
case 's':
{
char *string = va_arg(vp,char*);
print_str(string);
}
break;
default:
break;
}
}
break;
default:
putchar(c);
break;
}
format++;
}
52  va_end(vp);
53 }

理解可变参数很重要的是要理解函数栈帧的创建与销毁,里面涉及到参数是如何压栈,这方面内容在前面的博客:http://www.cnblogs.com/zhonglongbo/p/8392026.html

 

C语言中函数可变参数解析的更多相关文章

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

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

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

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

  3. C语言中的可变参数函数

    C语言编程中有时会遇到一些参数个数可变的函数,例如printf()函数,其函数原型为: int printf( const char* format, ...); 它除了有一个参数format固定以外 ...

  4. 【C/C++开发】C语言实现函数可变参数

    函数原型: int printf(const char *format[,argument]...)        返 回 值: 成功则返回实际输出的字符数,失败返回-1.  函数说明:        ...

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

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

  6. c语言中的# ## 可变参数宏 ...和_ _VA_ARGS_ _

    1.#假如希望在字符串中包含宏参数,ANSI C允许这样作,在类函数宏的替换部分,#符号用作一个预处理运算符,它可以把语言符号转化程字符串.例如,如果x是一个宏参量,那么#x可以把参数名转化成相应的字 ...

  7. python中函数的参数解析

    python中函数的各种参数梳理: 1.形参:函数定义时传入的参数 2.实参:函数调用时传入的参数 (有形参必传实参,形参里自身特点可不传的,可传可不传) 3.缺省参数:不传为默认值,传了会覆盖(下面 ...

  8. 各种语言中的可变参数(java、python、c++、javascript)

    索引: java python c++ js 1.Java public class Animal { // 接受可变参数的方法 void eat(String... Objects) { for ( ...

  9. python-函数中的参数类型和可变参数解析

    最近,在学习python,天天抱着廖雪峰的教材苦练,https://www.liaoxuefeng.com/ 但廖老毕竟是如此的才华盈溢,我这等小辈真是读起来教程都有些费力. 关于python-函数中 ...

随机推荐

  1. 《JAVA程序设计与实例》记录与归纳--继承与多态

    继承与多态 概念贴士: 1. 继承,即是在已经存在的类的基础上再进行扩展,从而产生新的类.已经存在的类成为父类.超类和基类,而新产生的类成为子类或派生类. 2. Java继承是使用已存在的类的定义作为 ...

  2. 《JAVA程序设计与实例》记录与归纳--类与对象

    类与对象 概念贴士: 1. 类必须先定义了才能使用.类是创建对象的模板,创建对象也叫类的实例化. 2. 在Java中,使用new关键字来创建对象,一般有一下3个步骤: 1)声   明:声明一个对象,包 ...

  3. NYOJ 题目77 开灯问题(简单模拟)

    开灯问题 时间限制:3000 ms  |            内存限制:65535 KB 难度:1           描述 有n盏灯,编号为1~n,第1个人把所有灯打开,第2个人按下所有编号为2 ...

  4. 【NOI2014】魔法森林

    为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为 1…n1…n,边标号为1…m1…m.初始时小E同学在 11 号节点,隐 ...

  5. bzoj 3874: [Ahoi2014]宅男计划

    Description  [故事背景] 自从迷上了拼图,JYY就变成了个彻底的宅男.为了解决温饱问题,JYY 不得不依靠叫外卖来维持生计. [问题描述] 外卖店一共有N种食物,分别有1到N编号.第i种 ...

  6. Ugly Numbers(STL应用)

    题目链接:http://poj.org/problem?id=1338 Ugly Numbers Time Limit: 1000MS   Memory Limit: 10000K Total Sub ...

  7. RabbitMQ 使用demo

    1.新建一个控制台应用程序:如图 2.代码如下: using RabbitMQ.Client;using RabbitMQ.Client.Events;using System;using Syste ...

  8. .NET MongoDB Driver 2.2 API注释

    主要内容 1 MongoClient 1.1构造函数 1.2 方法 2 IMongoDatabase 3 IMongoCollection 4 IMongoCollectionExtensions 5 ...

  9. i++是否原子操作?并解释为什么?

    都不是原子操作.理由: 1.i++分为三个阶段: 内存到寄存器寄存器自增写回内存这三个阶段中间都可以被中断分离开.  2.++i首先要看编译器是怎么编译的, 某些编译器比如VC在非优化版本中会编译为以 ...

  10. Deep Learning速成教程

          引言         深度学习,即Deep Learning,是一种学习算法(Learning algorithm),亦是人工智能领域的一个重要分支.从快速发展到实际应用,短短几年时间里, ...