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

标准库提供的一些参数的数目可以有变化的函数。例如我们很熟悉的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不符。这一调用的结果完全由编译程序和执行环境决定,得到的结果肯定不会是正确的。

函数参数中的3个点表示什么相关推荐

  1. Python函数参数中的冒号与箭头

    在一些Python的工程项目中,我们会看到函数参数中会有冒号,有的函数后面会跟着一个箭头,你可能会疑惑,这些都是什么东西? 其实函数参数中的冒号是参数的类型建议符,告诉程序员希望传入的实参的类型.函数 ...

  2. python函数定义中参数列表里的参数是_python函数参数中的/和*是什么意思?

    在python3.8之后函数参数中允许出现/和*号,/用来指明某些函数形参必须使用位置参数而非关键字参数的形式,*出现在函数参数中第一种含义可以表示为可变参数,一般写作*args:对于单独出现在参数中 ...

  3. 函数参数中带省略号的用法

     [转]函数参数中带省略号的用法 本文摘自CDSN<可变参数学习笔记>,原帖链接:http://topic.csdn.net/t/20041124/09/3582660.html 前言 ...

  4. C++中WINAPI函数参数中的IN和OUT

    在C++API函数参数中的in和out其实是一个宏,其中,in这个变量或参数是输入值,即要求必须给这个变量填写好以后提交给某个函数去执行. out这个变量的意思是输出值,即你不需要预先给它赋值,当函数 ...

  5. C++:在函数参数中使用++与--运算符

    C++:在函数参数中使用++与–运算符 进行C++的学习有一段时间了,今天做练习的时候遇到了一个在函数参数中使用带有++运算符表达式的问题,稍微研究了一下发现用到的知识点还不少,挺有意思的,这里正好进 ...

  6. 关于cmp函数参数中的符号(转)

    原文链接:https://blog.csdn.net/qie_wei/article/details/81135920 关于sort函数中的cmp函数有着不同的写法,以刚刚的整形元素比较为例 还有人是 ...

  7. C函数参数中的三个点

    原文链接一:http://hi.baidu.com/wjun520/blog/item/1678a11da07fe68086d6b653.html C++中有函数重载这种方法,以供我们调用时要可以不确 ...

  8. java中3|4_关于java:函数参数中3个点的含义是什么?

    本问题已经有最佳答案,请猛点这里访问. 我在读Android文档中的AsyncTask. private class DownloadFilesTask extends AsyncTask { pro ...

  9. python教程:函数参数中默认值及重要警告

    最有用的形式是对一个或多个参数指定一个默认值.这样创建的函数,可以用比定义时允许的更少的参数调用,比如: def ask_ok(prompt, retries=4, reminder='Please ...

最新文章

  1. 深入理解JavaScript系列(10):JavaScript核心(晋级高手必读篇)
  2. 获腾讯增持,B站二次元的商业化道路仍布满荆棘
  3. 计算机组成SRAM,静态读写存储器(SRAM)-计算机组成原理与汇编语言-电子发烧友网站...
  4. P5607-[Ynoi2013]无力回天NOI2017【线性基,线段树,树状数组】
  5. Java——集合(练习题)
  6. CF954I Yet Another String Matching Problem 并查集、FFT
  7. php判断有没有登录,请问thinkphp 怎么在每个方法里面判断是否登录?
  8. 2018-03-28 从人机交互角度看中文编程:#39;打开微信#39;
  9. SDOI2017R2PKUSC2017
  10. linux 自动运行.out,linux系统定时自动重启springboot项目
  11. 第4章——存储器、存储管理和高速缓存技术
  12. Docker Yearning + Inception SQL审核平台搭建
  13. 亚马逊ec2服务器上无法使用sudo执行npm命令的解决办法
  14. 股票交易数据接口是什么?
  15. 推荐五个免费UML建模工具
  16. 第007篇:易康(eCognition)中实现分类的继承
  17. 股指跨期套利基础学习
  18. 安装Ubuntu虚拟机
  19. android 仿微信聊天气泡显示图片,怎么实现微信聊天时的气泡图(一)
  20. 全新设计 水果忍者-穿靴子的猫官方中文版首发

热门文章

  1. c++实现字符串逆序输出
  2. CF1526B I Hate 1111- Codeforces Round #723 (Div. 2)
  3. r语言在linux下取数据,菜鸟第一步,跪在数据处:R语言读取数据
  4. java计算机毕业设计婚纱摄影网设计源码+mysql数据库+系统+lw文档+部署
  5. gradle项目中 build.gradle 配置文件详解(未完成)
  6. dns集群/ddns(dhcp+dns)安全动态域名解析
  7. 《大学数学手册》摘抄
  8. 关于GoldenGate 中的 BLR 补丁
  9. vscode Go傻瓜式代码注释生成插件-GoComment
  10. vue实现时间戳格式化