转载于网友的一片文章,写的很好!

标准库提供的一些参数的数目可以有变化的函数。例如我们很熟悉的printf,它需要有一个格式串,还应根据需要为它提供任意多个“其他参数”。这种函数被称作“具有变长度参数表的函数”,或简称为“变参数函数”。我们写程序中有时也可能需要定义这种函数。要定义这类函数,就必须使用标准头文件<stdarg.h>,使用该文件提供的一套机制,并需要按照规定的定义方式工作。本节介绍这个头文件提供的有关功能,它们的意义和使用,并用例子说明这类函数的定义方法。
         一个变参数函数至少需要有一个普通参数,其普通参数可以具有任何类型。在函数定义中,这种函数的最后一个普通参数除了一般的用途之外,还有其他特殊用途。下面从一个例子开始说明有关的问题。
        假设我们想定义一个函数sum,它可以用任意多个整数类型的表达式作为参数进行调用,希望sum能求出这些参数的和。这时我们应该将sum定义为一个只有一个普通参数,并具有变长度参数表的函数,这个函数的头部应该是(函数原型与此类似):
int sum(int n, ...)
        我们实际上要求在函数调用时,从第一个参数n得到被求和的表达式个数,从其余参数得到被求和的表达式。在参数表最后连续写三个圆点符号,说明这个函数具有可变数目的参数。凡参数表具有这种形式(最后写三个圆点),就表示定义的是一个变参数函数。注意,这样的三个圆点只能放在参数表最后,在所有普通参数之后。
        为了能在变参数函数里取得并处理不定个数的“其他参数”,头文件<stdarg.h>提供了一套机制。这里提供了一个特殊类型va_list。在每个变参数函数的函数体里必须定义一个va_list类型的局部变量,它将成为访问由三个圆点所代表的实际参数的媒介。下面假设函数sum里所用的va_list类型的变量的名字是vap。在能够用vap访问实际参数之前,必须首先用“函数”va_start做这个变量初始化。函数va_start的类型特征可以大致描述为:
va_start(va_list vap, 最后一个普通参数)
实际上va_start通常并不是函数,而是用宏定义实现的一种功能。在函数sum里对vap初始化的语句应当写为:
va_start(vap, n);
在完成这个初始化之后,我们就可以通过另一个宏va_arg访问函数调用的各个实际参数了。宏va_arg的类型特征可以大致地描述为:
类型 va_arg(va_list vap, 类型名)
        在调用宏va_arg时必须提供有关实参的实际类型,这一类型也将成为这个宏调用的返回值类型。对va_arg的调用不仅返回了一个实际参数的值(“当前”实际参数的值),同时还完成了某种更新操作,使对这个宏va_arg的下次调用能得到下一个实际参数。对于我们的例子,其中对宏va_arg的一次调用应当写为:
v = va_arg(vap, int);
这里假定v是一个有定义的int类型变量。
        在变参数函数的定义里,函数退出之前必须做一次结束动作。这个动作通过对局部的va_list变量调用宏va_end完成。这个宏的类型特征大致是:
void va_end(va_list vap);
下面是函数sum的完整定义,从中可以看到各有关部分的写法:
int sum(int n, ...) {
      va_list vap;
       int i, s = 0;
       va_start(vap, n);
       for (i = 0; i < n; i++) s += va_arg(vap, int);
       va_end(vap);
       return s;
}
这里首先定义了va_list变量vap,而后对它初始化。循环中通过va_arg取得顺序的各个实参的值,并将它们加入总和。最后调用va_end结束。
下面是调用这个函数的几个例子:
k = sum(3, 5+8, 7, 26*4);
m = sum(4, k, k*(k-15), 27, (k*k)/30);
         在编写和使用具有可变数目参数的函数时,有几个问题值得注意。首先,虽然在上面描述了头文件所提供的几个宏的“类型特征”,实际上这仅仅是为了说明问题。因为实际上我们没办法写出来有关的类型,系统在预处理时进行宏展开,编译时即使发现错误,也无法提供关于这些宏调用的错误信息。所以,在使用这些宏的时候必须特别注意类型的正确性,系统通常无法自动识别和处理其中的类型转换问题。
        第二:调用va_arg将更新被操作的va_list变量(如在上例的vap),使下次调用可以得到下一个参数。在执行这个操作时,va_arg并不知道实际有几个参数,也不知道参数的实际类型,它只是按给定的类型完成工作。因此,写程序的人应在变参数函数的定义里注意控制对实际参数的处理过程。上例通过参数n提供了参数个数的信息,就是为了控制循环。标准库函数printf根据格式串中的转换描述的数目确定实际参数的个数。如果这方面信息有误,函数执行中就可能出现严重问题。编译程序无法检查这里的数据一致性问题,需要写程序的人自己负责。在前面章节里,我们一直强调对printf等函数调用时,要注意格式串与其他参数个数之间一致性,其原因就在这里。
        第三:编译系统无法对变参数函数中由三个圆点代表的那些实际参数做类型检查,因为函数的头部没有给出这些参数的类型信息。因此编译处理中既不会生成必要的类型转换,也不会提供类型错误信息。考虑标准库函数printf,在调用这个函数时,不但实际参数个数可能变化,各参数的类型也可能不同,因此不可能有统一方式来描述它们的类型。对于这种参数,C语言的处理方式就是不做类型检查,要求写程序的人保证函数调用的正确性。
假设我们写出下面的函数调用:
k = sum(6, 2.4, 4, 5.72, 6, 2);
        编译程序不会发现这里参数类型不对,需要做类型转换,所有实参都将直接传给函数。函数里也会按照内部定义的方式把参数都当作整数使用。编译程序也不会发现参数个数与6不符。这一调用的结果完全由编译程序和执行环境决定,得到的结果肯定不会是正确的。

C语言函数中的3个点 ...有什么作用相关推荐

  1. 【IT】C语言函数中怎么返回一个二维数组

    C语言函数中怎么返回一个二维数组

  2. c语言函数中调用的参数太多

    c语言函数中调用的参数太多问题 问题展示 问题分析 解决方法 问题展示 (图中是我遇到的情况) 问题分析 大家可以看到,在函数中,指针变量和后面的整数变量都成了灰色 解决方法 图中问题只需将中文逗号, ...

  3. 【C】C语言函数中的变量(包括:作用域、存储类型)

    局部变量和全局变量 C语言中变量按其作用域分,可分为局部变量和全局变量,具体的解释为: 局部变量(内部变量):在定义它的函数内有效,但是函数返回后失效: 全局变量(外部变量):在所有源文件内均有效.在 ...

  4. C语言-----函数中的变量

    目录 1.局部变量与全局变量 1.1 局部变量 1.2 全局变量 2.变量的存储类别 2.1 自动类型 (auto) 2.2 寄存器类型 (register) 2.3 静态类型 (static) 2. ...

  5. c语言函数中使用指针变量,C语言函数指针变量

    C语言函数指针变量 导语:在C语言中规定,一个函数总是占用一段连续的内存区, 而函数名就是该函数所占内存区的首地址. 这就是函数指针变量.下面是相关介绍,仅供参考! 在C语言中规定,一个函数总是占用一 ...

  6. C/C++语言函数中参数的入栈顺序

    对于函数,之前认为会用就行了,对其中的原理并不是很了解,就比如函数中参数的入栈顺序(在这说明一下,函数的参数是保存在栈中的,还有一些局部变量也是存放在栈中),这个问题来源于某互联网的面试题,当然答得很 ...

  7. 消失的字符串——c语言函数中的数据存储方式以及字符串实现

    2019独角兽企业重金招聘Python工程师标准>>> 事情是这样的,最近在学习翁老师的Mooc之C语言程序设计,插一句,真的是好老师,讲的真的太好了,在c里的字符串这里,翁老师演示 ...

  8. %ld在c语言函数中个的作用,C语言的%ld、%d、%U是啥意思啊?

    满意答案 wkwh135 推荐于 2019.09.03 采纳率:47%    等级:9 已帮助:1664人 printf()函数是格式化输出函数, 一般用于向标准输出设备按规定格式输出信息.print ...

  9. C语言 函数中返回数组

    函数无法直接返回数组,要使用static定义数组,再返回.这样相当于定义全局变量. static double v[10]={0}; ...... return v;

最新文章

  1. sql数据库JDBCTemplate和JPA使用
  2. Investigating Div-Sum Property【数位DP】
  3. 爬虫-练习引导-豆办电影爬取-json数据的处理
  4. vba校对不同工作薄中的内容
  5. ionic 性能优化
  6. 公众号jdk 获取手机号_怎样快速获取使用国庆节公众号文章的素材和模板
  7. 蓝桥杯 ALGO-112 算法训练 暗恋
  8. jquery根据滚动像素显示隐藏顶部导航条
  9. python语言三角函数_python中三角函数_Python中的三角函数
  10. 异数OS 织梦师-水母(一)--消息队列篇
  11. w ndows热键,Window 10 优雅的快捷键
  12. 30. Python------(面向对象练习)搬家具
  13. cmd命令查询电脑序列号_如何使用指令提示符查看电脑序列号
  14. python爬虫使用session保持登录状态
  15. 黑五节日营销,Facebook广告投放指南
  16. 使用aws跑深度学习_11. 附录 - 11.4. 使用AWS运行代码 - 《动手学深度学习》 - 书栈网 · BookStack...
  17. MySQL 8.0原理与实战一网打尽,甲骨文数据库专家硬刚5年之作
  18. 输入一批整数,以0为结束标志,找出最大数和最小数所在的位置,并把二者对调
  19. 实现人民币的大小写转换
  20. 请移驾ray7hu.com

热门文章

  1. CSDN实训 - Java模拟二次验证码(动态令牌)
  2. VOC、COCO数据集类别
  3. 64位ubantu 16.04.2系统安装谷歌浏览器过程
  4. total commander 自定义快捷键
  5. keep sb updated_keep you update 和keep you updated哪个正确
  6. xml引入另一个xml文件_什么是XML文件(以及如何打开一个XML文件)?
  7. elasticsearch 过期数据自动删除Java代码
  8. 计量经济学及Stata应用 陈强 第十章工具变量法习题10.6
  9. 【转】跨终端实践-天猫试戴的解决方案
  10. 利用python进行平行束FBP重建结果