文档章节

第16章 C预处理器和C库 16.13 可变参数:stdarg.h

idreamo
 idreamo
发布于 2017/08/01 07:20
字数 1238
阅读 14
收藏 0

本章前面讨论了可变宏,该宏接受可变个数的参数。

头文件stdarg.h为函数提供了类似的能力。不过使用方法稍微复杂一些。必须按如下步骤进行:

1、在函数原型中使用省略号

2、在函数定义中创建一个va_list类型的变量

3、用宏将该变量初始化为一个参数列表

4、用宏该问这个参数列表

5、用宏完成清理工作

现在详细讨论这些步骤。这类函数的原型应具有一个参量列表,参量列表中至少有一个后跟省略号的参量

void f1(int n,...);            //合法

int f2(int k,const char *s,...)    //合法

char f3(char c1,...,char c2)     //无效,省略号不是最后一个参量

double f3( );                         //无效,没有任何参量

最右边的参量(省略号前)起着特殊作用;ANSI 使用parmN表示该参量。前例中,第一种情况下parmN为n,第二种情况下parmNo为k。传递给该参量的实际参数值将是省略号部分代表的参数个数。

例如,前面的f1()函数原型可以这样使用:

f1(2,200,400);      //2个额外的参数

f1(4,13,117,18,23); //4个额外的参数

接下来,在头文件stdarg.h中声明的va_list类型代表一种数据对象该数据对象用于存放参量列表中省略号部分代表的参量。可变函数定义的起始部分应像下面这样:

double sum(int lim,...)

{

    va_list ap;    //声明用于存放参数的变量

本例中,lim为参量parmN,由它来指定可变参数列表中的参数个数。

然后,函数将使用stdarg.h中定义的宏va_start( )把参数列表复制到va_list变量中宏va_start()有两个参数:va_list类型的变量和参量parmN。我们接着前一个例子讨论,va_list类型的变量为ap,参量parmN为lim,因此,对va_start()的调用应如下所示:

va_start(ap,lim);    //把ap初始化为参数列表

下一步是访问参数列表中的内容这一步涉及宏va_arg()的使用该宏接受两个参数:一个va_list类型的变量,一个类型名。

第一次调用va_arg()时,它返回参数列表的第一项,下次调用返回第二项,依此类推类型参数指定返回值的类型。例如,如果参数列表中第一个参数为double类型,第二个为int类型,那么可使用如下语句:

double tic;
int toc;
...
tic = va_arg(ap,double);    //取得第一个参数
toc = va_arg(ap,int);       //取得第二个参数

注意,实际参数的类型必须与说明的类型相匹配。如果第一个参数是10.0,那么前面的tic部分的代码正常工作,但是如果参数是10,代码就可能无法工作。这里不会像赋值过程那样进行double到int的自动转换。

最后,应使用va_end()完成清理工作。例如,释放动态分配的用于存放参数的内存。该宏接受一个va_list变量作为参数:

va_end(ap);    //清理工作

此后,只有用va_start()重新对ap初始化后,才能使用变量ap。

因为va_arg()不提供后退回先前参数的方法,所以保存va_list变量的副本是会有用的。C99专门为此添加了宏va_copy()。该宏的两个参数为va_list类型变量,它将第二个参数复制到第一个参数中

va_list ap;
va_list apcopy;
double
double tic;
int toc;
...
va_start(ap,lim);  //把ap初始化为参数列表
va_copy(apcopy,ap);//apcopy是ap的一个副本
tic = va_arg(ap,double);  //取得第一个参数
toc = va_arg(ap,int);     //取得第二个参数

此时,虽然从ap中删除了前两项,但还可以从apcopy中重新获取这两项

程序清单16.18的简短示例程序说明了创建一个这样的函数的方法,该函数对可变参数进行求和运算。sum()的第一个参数是要进行求和运算的项目的个数。

程序清单16.18 varargs.c  程序

//varargs.c  --使用可变个数的参数
#include <stdio.h>
#include <stdarg.h>
double sum(int ,...);

int main()
{
    double s,t;

    s = sum(3,1.1,2.5,13.3);
    t = sum(6,1.1,2.1,13.1,4.1,5.1,6.1);
    printf("return value for "
           "sum(3,1.1,2.5,13.3):                      %g\n",s);
    printf("return value for "
           "sum(6,1.1,2.1,13.1,4.1,5.1,6.1):          %g\n",t);

    return 0;
}

double sum(int lim,...)
{
    va_list ap;      //声明用于存放参数的变量
    double tot=0;
    int i;
    va_start(ap,lim);//把ap初始化为参数列表
    for(i=0;i<lim;i++)
        tot+=va_arg(ap,double);//访问参数列表中的每一项
    va_end(ap);    //清理工作
    return tot;
}

输出如下:

return value for sum(3,1.1,2.5,13.3):                      16.9
return value for sum(6,1.1,2.1,13.1,4.1,5.1,6.1):          31.6

可以发现,第一次调用sum()时对3个数求和,第二次调用时对6个数求和。

总之,可变函数的用法比可变宏更复杂,但是函数的应用范围更广。

© 著作权归作者所有

idreamo
粉丝 18
博文 139
码字总数 224743
作品 0
青岛
产品经理
私信 提问
Head First C学习日志 第七章 创建可变参数的函数

让函数能伸缩 为了让函数可以接收可变个数/类型的参数,在此引入 可变参数的函数 宏与函数 宏用来在编译前重写代码,这里引入几个宏va_start,va_list,va_arg和va_end,宏看起来和函数类似,...

AlexTuan
2016/03/24
57
0
Windows下C/C++可变参数宏实现技巧

Windows下C/C++可变参数宏实现技巧 在开发过程中,有很多阶段,每个阶段可能会注重不同的重点,我们可能会在不同阶段让程序输出或者打印不同的信息以反应运行的情况,所以我们必须分阶段的使...

Start-up
2012/10/19
367
0
C Primer Plus 第11章 11.7 ctype.h字符函数和字符串

第7章“C控制语句 分支和跳转”介绍了ctype.h系列字符相关的函数。这些函数不能被 应用于整个字符串,但是可以被应用于字符串中的个别字符。程序清单11.26定义了一个函数,它把toupper( )函数...

idreamo
2016/08/27
52
0
[编程语言]C陷阱与缺陷

内容摘要 作者以自己1985年在Bell实验室时发表的一篇论文为基础,结合自己的工作经验扩展成为这本对C程序员具有珍贵价值的经典著作。写作本书的出发点不是要批判C语言,而是要帮助C程序员绕过...

21gprs
2014/05/23
0
0
C语言的变参函数设计

在C语言中,函数参数的传递方式有值传和址传.值传是把实参的一个专用的、临时的复制值给被调函数中相应的形参被调用函数使用、修改这个传来的复制值,不会影响实参的值.址传则是把变量(实参)...

嗯哼9925
2017/12/25
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周四乱弹 —— 当你简历注水但还是找到了工作

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @花间小酌 :#今日歌曲推荐# 分享成龙的单曲《男儿当自强》。 《男儿当自强》- 成龙 手机党少年们想听歌,请使劲儿戳(这里) @hxg2016 :刚在...

小小编辑
今天
3K
22
靠写代码赚钱的一些门路

作者 @mezod 译者 @josephchang10 如今,通过自己的代码去赚钱变得越来越简单,不过对很多人来说依然还是很难,因为他们不知道有哪些门路。 今天给大家分享一个精彩的 GitHub 库,这个库整理...

高级农民工
昨天
5
0
用好项目管理工具,人人都可以成为项目经理

现在市面上的项目管理工具越来越多了,但是大多数都是一些协同工具或轻量项目管理工具。如果是多团队、跨部门使用或者企业级的项目管理,从管理思想到工具运用,需要适应企业的业务流程体系,...

cs平台
昨天
13
0
只需一步,在Spring Boot中统一Restful API返回值格式与统一处理异常

统一返回值 在前后端分离大行其道的今天,有一个统一的返回值格式不仅能使我们的接口看起来更漂亮,而且还可以使前端可以统一处理很多东西,避免很多问题的产生。 比较通用的返回值格式如下:...

晓月寒丶
昨天
70
0
区块链应用到供应链上的好处和实际案例

区块链可以解决供应链中的很多问题,例如记录以及追踪产品。那么使用区块链应用到各产品供应链上到底有什么好处?猎头悬赏平台解优人才网小编给大家做个简单的分享: 使用区块链的最突出的优...

猎头悬赏平台
昨天
32
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部