原文链接一:http://hi.baidu.com/wjun520/blog/item/1678a11da07fe68086d6b653.html

C++中有函数重载这种方法,以供我们调用时要可以不确定实参的个数,其实 C 语言也可以,而且更高明!

我们在stdio.h 中可以看到 printf() 函数的原型:

int printf(char * format,...)

事实上,我们如果要写这样的函数也可以类似的写,那么在定义函数时用上这个符号“ ... ” ,它叫占位符,喊它 “ 三个点 ” 也可以,只要你愿意!那么我可以这样定义我的函数:

fun(int a,...)

{   }

只要上课认真听了的同学(傻瓜除外)都知道,这是个空函数,它是什么都不做的,光这样写还不行的,具体应该怎样定义呢?

且听我介绍3 个小东东:

1、 va_list

2、 va_arg()

3、 va_start()

在学习这3 个小东东之前,我们先回忆一下, C 语言是怎么操作文件时,是怎么样处理内存中的数据的呢?学习文件操作时,我们提到了“流”的概念,我们用指针指向数据所在的内存地址,再一个一个的操作。

学习指针时,我们知道有函数指针这个东东,不是指针函数而是函数打针哦!(呵呵,我的同学如果还记得就当复习一下,不要嫌我啰嗦^_^ )。我们记得程序在执行时,会将函数存储到内存中去。现在深入的讲一点点,存储函数时,参数传递的过程是怎样实现的呢?所谓的形式参数(局部变量)实质上又是什么呢?把这些问题连起来想想,想通了,你的思维势如破竹!

在调用函数时,程序同样会把实参传入,在函数存储区保存起来,如果有很多参数,将一起保存起来。

这时候就要用到va_list 了,这是个类型定义,我们可以把它理解成一个指针,它指向第一个参数的地址。

如果,我们这样定义: va_list pp ;

则pp 就是这样一种变量,它是指向所有参数中的第一个参数的。它不同于一般的指针变量,它是个复合变量,什么是复合变量啊?结构体类型的嘛,呵呵。如果 a 是第一个参数,能不能写成 pp=a 呢?

假设我定义了char d[]="ruixin",e[]="gelin"; 我要把 e 的值赋给 d ,能不能写成 d=e 呢?得用 strcpy() ,是吧!呵呵,一样的道理,这儿我们也用一个函数来实现,它就是 va_start();

如果这样写:va_start(pp,a);

那么pp 就指向第一个参数 a 了,并且可得到 a 的类型 int 。

这时候如果有下一个参数,就需要使pp 指向下一个参数,并且得到它的类型。同样需要使用函数来实现,这个函数是: va_arg()

可以这样写:va_arg(pp, 类型 ) ,这样 pp 就指向一个参数,并且可以得到那个参数的类型了。

注意!类型非常重要,学过指针的都应该清楚,指针的类型如果弄错的话,位置正确,取出来的数可能也是乱七八糟的。

下面我们看一个简单的例子:

#include <stdio.h>

#include<stdarg.h>

void fun(int a,...)

{

va_list pp;

int n=1;//使用 n 计量参数个数

va_start(pp,a);

do

{

printf("第 %d 个参数 =%d/n",n++,s);

a=va_arg(pp,int);//使 pp 指向下一个参数,将下一个参数的值赋给变量 a

}

while (a!=0);//直到参数为 0 时停止循环

}

main()

{

fun(20,40,60,80,0);

}

注意!

一定要有上面两个文件包含命令,因为程序中用到的那3个小东东都在那个文件里。其实真正意义上应该说那是函数,实质上那不过是两个宏,呵呵。

什么是宏,什么是函数,不是这儿要讲的,也和这没太大关系。写出来,仅仅是回答一个为什么……

原文链接二:http://blog.csdn.net/joliny/archive/2008/04/28/2340299.aspx

VA_LIST 是在C语言中解决变参问题的一组宏

VA_LIST的用法:     
       (1)首先在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针
      (2)然后用VA_START宏初始化变量刚定义的VA_LIST变量,这个宏的第二个参数是第一个可变参数的前一个参数,是一个固定的参数。
       (3)然后用VA_ARG返回可变的参数,VA_ARG的第二个参数是你要返回的参数的类型。
       (4)最后用VA_END宏结束可变参数的获取。然后你就可以在函数里使用第二个参数了。如果函数有多个可变参数的,依次调用VA_ARG获取各个参数。

VA_LIST在编译器中的处理:
1)在运行VA_START(ap,v)以后,ap指向第一个可变参数在堆栈的地址。
(2)VA_ARG()取得类型t的可变参数值,在这步操作中首先apt = sizeof(t类型),让ap指向下一个参数的地址。然后返回ap-sizeof(t类型)的t类型*指针,这正是第一个可变参数在堆栈里的地址。然后用*取得这个地址的内容。
(3)VA_END(),X86平台定义为ap = ((char*)0),使ap不再指向堆栈,而是跟NULL一样,有些直接定义为((void*)0),这样编译器不会为VA_END产生代码,例如gcc在Linux的X86平台就是这样定义的。

要注意的是:由于参数的地址用于VA_START宏,所以参数不能声明为寄存器变量,或作为函数或数组类型。

使用VA_LIST应该注意的问题:
   (1)因为va_start, va_arg, va_end等定义成宏,所以它显得很愚蠢,可变参数的类型和个数完全在该函数中由程序代码控制,它并不能智能地识别不同参数的个数和类型. 也就是说,你想实现智能识别可变参数的话是要通过在自己的程序里作判断来实现的.
    (2)另外有一个问题,因为编译器对可变参数的函数的原型检查不够严格,对编程查错不利.不利于我们写出高质量的代码。

小结:可变参数的函数原理其实很简单,而 VA系列是以宏定义来定义的,实现跟堆栈相关。我们写一个可变函数的C函数时,有利也有弊,所以在不必要的 场合,我们无需用到可变参数,如果在C++里,我们应该利用C++多态性来实现可变参数的功能,尽量避免用C语言的方式来实现。

C函数参数中的三个点相关推荐

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

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

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

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

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

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

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

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

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

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

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

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

  7. 函数参数中的3个点表示什么

    转载于网友的一片文章,写的很好! 标准库提供的一些参数的数目可以有变化的函数.例如我们很熟悉的printf,它需要有一个格式串,还应根据需要为它提供任意多个"其他参数".这种函数被 ...

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

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

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

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

最新文章

  1. popWindow 出现点击上下空白区域消失点击左右空白区域不消失弹框的处理方法
  2. Python-爬取音悦台MV列表以及反爬虫方法
  3. SpringBoot中使用thymeleaf模板时select下拉框怎样查询数据库赋值
  4. 计算机视觉 - 知识点总结(面试、笔试)
  5. linux 设备驱动程序开发 第3版_Chapter2_The Current Process
  6. 专业程序员必知必会技巧:驯服复杂代码
  7. CF1486D Max Median
  8. 阻止函数源码在控制台输出
  9. PingCAP创始人刘奇:TiDB设计理念进化与大规模实践
  10. 复用管脚_如何实现UART的分时复用
  11. ios 仿微信,短信聊天气泡
  12. 【生活】深圳驾考经历
  13. 读书笔记:谁都可以进外企
  14. 电机集电环是如何更换与运行的
  15. JavaSE(二)-抽象类
  16. java判断车牌号正确性
  17. 怎样将cad布局导出来_CAD如何将布局里面的内容输出为 *.dwg文件? | 怎么把cad 中的数据输出...
  18. unity 内部嵌入的网页插件中播放视频网页的问题
  19. SpringBoot启动报错Could not resolve placeholder ‘XXX.XXX‘ in value
  20. python 导入模型_scikit-learn系列之如何存储和导入机器学习模型

热门文章

  1. 【知识图谱】语义网络,语义网,链接数据和知识图谱
  2. 小图标的使用(插入icon图标)
  3. 12星座谈恋爱:说分手,很容易
  4. RK3328启动失败解决记录
  5. 有了ElasticSearch为什么还要用MySQL?-这篇文章告诉你
  6. 知识计算机硬件 教学设计,计算机硬件教案
  7. Linux配置SSH服务器
  8. 《MySQL必知必会》的读后感
  9. MAVROS的plugin到底是什么意思?plugin中文意思是插件
  10. canel-1.1.5 canal.deployer安装