在c语言中使用变长参数最常见的就是下面两个函数了:

int printf(const char *format, ...);int scanf(const char *format, ...);

  那他们是怎样实现支持变成参数的呢?在使用变长参数的函数(这里假设是func)实现部分其实用到了stdarg.h里面的多个宏来访问那些不确定的参数,它们分别是:

void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_end(va_list ap);

  假设lastarg是func的最后一个具名参数,即在func函数定义中...之前的那个参数(在printf中lastarg是format),在func中首先定义一个变量:

va_list ap

  这个变量以后会依次指向各个可变参数。ap在使用之前必须用宏va_start初始化一次,如下所示:

va_start(ap, lastarg);

其中lastarg是func中的最后一个具名参数。然后就可以用va_arg来获得下一个不定参数(前提是知道这个不定参数的类型type):

type next = va_arg(ap, type)

  最后就是用宏va_end来清理现场。

  下面我们来自己实现一个可变参数的函数:

 1 #include <stdio.h>2 #include <stdarg.h>3 4 void func(char *fmt, ...)5 {6     va_list ap;7 8     va_start(ap, fmt);9
10     while (*fmt)
11     {
12         switch(*fmt)
13         {
14             case 'd':
15                 fprintf(stdout, "%d\n", (int)va_arg(ap, int));
16                 break;
17             case 'c':
18                 fprintf(stdout, "%c\n", (char)va_arg(ap, int));
19                 break;
20             case 's':
21                 fprintf(stdout, "%s\n", (char *)va_arg(ap, char *));
22                 break;
23             default:
24                 fprintf(stderr, "error fmt\n");
25         }
26         fmt ++;
27     }
28     va_end(ap);
29 }
30
31 int main ( int argc, char *argv[] )
32 {
33     func("dcs", 10, 's', "hello");
34     return 0;
35 }               /* ----------  end of function main  ---------- */

  输出结果:

10
s
hello

  可以看到上面的程序完成按我们的意愿实现了变长参数的访问,通过前面的fmt来控制下一个不定参数的类型。那这三个宏是怎样实现对不定参数访问的呢?下面来看看它们是怎么实现的:

  va_list实际就是一个指向各个不定参数的指针,由于参数的类型是不确定的,所以可以定义va_list为void *或者char *类型,即

#define va_list void *

  va_start就是将va_list指向函数最后一个具名参数lastarg后面的位置,这个位置就是第一个不定参数。

#define va_start(ap, lastarg) \
(ap = (va_list)&lastarg + sizeof(lastarg))

  va_arg获取当前不定参数的值,根据当前参数的类型的大小移动指针指向下一个不定参数。

#define va_arg(ap, type) \
(*(type *)((ap += sizeof(type)) - sizeof(type)))

  va_end将指针清0。

#define va_end(ap) (ap=(va_list)0)

  本质上其实就是靠前面lastarg来控制不定参数的类型,va_list变量来指向不定参数的地址,然后根据lastarg一个一个的获取不定参数。

转载于:https://www.cnblogs.com/gildoringlorin/p/3965787.html

【转载】c语言中的可变参数编程相关推荐

  1. c语言va_start函数,va_start和va_end,以及c语言中的可变参数原理

    FROM:http://www.cnblogs.com/hanyonglu/archive/2011/05/07/2039916.html 本文主要介绍va_start和va_end的使用及原理. 在 ...

  2. c语言理解参数,c语言中对可变参数列表的简单理解

    函数原型中一般情况下参数的数目是固定的,但是如果想在不同的时候接收不定数目的参数时该怎么办呢?c语言提供了可变参数列表来实现. 可变参数列表是通过宏来实现的,这些宏定义在stdarg.h的头文件中.头 ...

  3. C语言中函数可变参数解析

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

  4. 编写可变参数函数 c语言,C语言中编写可变参数函数

    通过stdarg.h头文件为函数提供了定义可变参数列表的能力.声明一个可变参数的函数类似: void f1(int n,...); 其中n表示参数列表个数,而用省略号来表示未知参数列表.stdarg. ...

  5. 【C语言进阶深度学习记录】三十九 C语言中的可变参数(参数可变的函数)

    用过printf()函数的热都知道,printf的参数可以有多个,它是可变的,根据我们输出参数的类型以及个数的不同来确定参数.今天来学习C语言中参数可变的函数是如何实现的. 文章目录 1 可变参数 2 ...

  6. ​Swift语言中为外部参数设置默认值可变参数常量参数变量参数输入输出参数

    ​Swift语言中为外部参数设置默认值可变参数常量参数变量参数输入输出参数 7.4.4  为外部参数设置默认值 开发者也可以对外部参数设置默认值.这时,调用的时候,也可以省略参数传递本文选自Swift ...

  7. C,C++中使用可变参数

    可变参数即表示参数个数可以变化,可多可少,也表示参数的类型也可以变化,可以是int,double还可以是char*,类,结构体等等.可变参数是实现printf(),sprintf()等函数的关键之处, ...

  8. C# 中的可变参数方法(VarArgs)

    首先需要明确一点:这里提到的可变参数方法,指的是具有 CallingConventions.VarArgs 调用约定的方法,而不是包含 params 参数的方法.可以通过MethodBase.Call ...

  9. C语言学习笔记---可变参数

      在C语言中有些函数传递进入的参数数量是不固定的,最常见的就是 printf() 函数,它就是利用可变参数来实现任意格式的数据打印功能. 我们自己也可以编写一个参数可变的函数,每次动态的检测传入的参 ...

最新文章

  1. UI设计需要报培训班学习吗
  2. 九、springboot整合rabbitMQ
  3. 数学大神攻克猜字游戏Wordle,求解算法成绩逼近理论极限,连信息论都用上了...
  4. 运行caffe自带的mnist实例教程
  5. 小白级OpenCV入门
  6. em算法怎么对应原有分类_[PRML]序列数据 HMM维特比算法及扩展
  7. c语言 指针 数组还能这么用 ?
  8. 第三次学JAVA再学不好就吃翔(part62)--Integer类
  9. 分区和分片的区别_PHP: 分区和分片 - Manual
  10. sql 标量子查询_SQL Server 2017:标量子查询简化
  11. JSR-303 数据校验学习
  12. 简单机器学习系统的构建以及对于不对称性的分类介绍和性能评价
  13. 软件测试cpu性能测试案例,cpu测试(cpu性能测试软件)
  14. Java刷题面试系列习题(三)
  15. html 文字竖着排引号,文字竖排演示
  16. halcon模板匹配实践(5)使用橡皮擦涂抹功能实现减少模板匹配特征
  17. 聚类算法之k-均值,k-中心点
  18. JVM-内存区域与OOM
  19. android 网络文件系统,android在手机上的文件系统框架的阐述
  20. 蓝牙软件测试指标,蓝牙测试简介

热门文章

  1. 「 Modbus-RTU报文解析」解析03、06、10功能码报文示例
  2. DSP学习-- UTC转UNIX时间戳
  3. Hi3516A开发--使用root模式登录Ubuntu14.04
  4. 用python内置函数算复杂度吗_番外篇: Python 面试感受
  5. web前端开发入门基础教程系列
  6. 【译】 Diving Into The Ethereum VM Part 6 - How Solidity Events Are Implemented
  7. 权益证明协议中的拜占庭容错
  8. Android内存分析工具:Memory Profiler
  9. linux日志绕接,[判断题] 绕接式保安接线排按结构分为固定式和旋转式。
  10. java堆外内存6_Java堆外内存排查小结