深入浅出可变参数函数的使用技巧本文主要介绍可变参数的函数使用,然后分析它的原理,程序员自己如何对它们实
现和封装,最后是可能会出现的问题和避免措施。

VA函数(variable argument function),参数个数可变函数,又称可变参数函数
。C/C++编程中,系统提供给编程人员的va函数很少。*printf()/*scanf()系列函数
,用于输入输出时格式化字符串;exec*()系列函数,用于在程序中执行外部文件(
main(int argc,char*argv[]算不算呢,与其说main()也是一个可变参数函数,倒不
如说它是exec*()经过封装后的具备特殊功能和意义的函数,至少在原理这一级上有
很多相似之处)。由于参数个数的不确定,使va函数具有很大的灵活性,易用性,对
没有使用过可变参数函数的编程人员很有诱惑力;那么,该如何编写自己的va函数
,va函数的运用时机、编译实现又是如何。作者借本文谈谈自己关于va函数的一些
浅见。

一、 从printf()开始

从大家都很熟悉的格式化字符串函数开始介绍可变参数函数。

原型:int printf(const char * format, ...);

参数format表示如何来格式字符串的指令,…

表示可选参数,调用时传递给"..."的参数可有可无,根据实际情况而定。

系统提供了vprintf系列格式化字符串的函数,用于编程人员封装自己的I/O函数。

int vprintf / vscanf(const char * format, va_list ap); // 从标准输入/输出
格式化字符串

int vfprintf / vfsacanf(FILE * stream, const char * format, va_list ap);
// 从文件流

int vsprintf / vsscanf(char * s, const char * format, va_list ap); // 从
字符串

// 例1:格式化到一个文件流,可用于日志文件

FILE *logfile;

int WriteLog(const char * format, ...)

{

va_list arg_ptr;

va_start(arg_ptr, format);

int nWrittenBytes = vfprintf(logfile, format, arg_ptr);

va_end(arg_ptr);

return nWrittenBytes;

}

…

// 调用时,与使用printf()没有区别。

WriteLog("%04d-%02d-%02d %02d:%02d:%02d  %s/%04d logged out.",

nYear, nMonth, nDay, nHour, nMinute, szUserName, nUserID);

同理,也可以从文件中执行格式化输入;或者对标准输入输出,字符串执行格式化
。

在上面的例1中,WriteLog()函数可以接受参数个数可变的输入,本质上,它的实现
需要vprintf()的支持。如何真正实现属于自己的可变参数函数,包括控制每一个传
入的可选参数。

二、 va函数的定义和va宏

C语言支持va函数,作为C语言的扩展--C++同样支持va函数,但在C++中并不推荐使
用,C++引入的多态性同样可以实现参数个数可变的函数。不过,C++的重载功能毕
竟只能是有限多个可以预见的参数个数。比较而言,C中的va函数则可以定义无穷多
个相当于C++的重载函数,这方面C++是无能为力的。va函数的优势表现在使用的方
便性和易用性上,可以使代码更简洁。C编译器为了统一在不同的硬件架构、硬件平
台上的实现,和增加代码的可移植性,提供了一系列宏来屏蔽硬件环境不同带来的
差异。

ANSI C标准下,va的宏定义在stdarg.h中,它们有:va_list,va_start(),va_ar
g(),va_end()。

// 例2:求任意个自然数的平方和:

int SqSum(int n1, ...)

{

va_list arg_ptr;

int nSqSum = 0, n = n1;

va_start(arg_ptr, n1);

while (n > 0)

{

nSqSum += (n * n);

n = va_arg(arg_ptr, int);

}

va_end(arg_ptr);

return nSqSum;

}

// 调用时

int nSqSum = SqSum(7, 2, 7, 11, -1);

可变参数函数的原型声明格式为:

type VAFunction(type arg1, type arg2, … );

参数可以分为两部分:个数确定的固定参数和个数可变的可选参数。函数至少需要
一个固定参数,固定参数的声明和普通函数一样;可选参数由于个数不确定,声明
时用"…"表示。固定参数和可选参数公同构成一个函数的参数列表。

借助上面这个简单的例2,来看看各个va_xxx的作用。

va_list arg_ptr:定义一个指向个数可变的参数列表指针;

va_start(arg_ptr, argN):使参数列表指针arg_ptr指向函数参数列表中的第一个
可选参数,说明:argN是位于第一个可选参数之前的固定参数,(或者说,最后一
个固定参数;…之前的一个参数),函数参数列表中参数在内存中的顺序与函数声
明时的顺序是一致的。如果有一va函数的声明是void va_test(char a, char b, c
har c, …),则它的固定参数依次是a,b,c,最后一个固定参数argN为c,因此就是
va_start(arg_ptr, c)。

va_arg(arg_ptr, type):返回参数列表中指针arg_ptr所指的参数,返回类型为ty
pe,并使指针arg_ptr指向参数列表中下一个参数。

va_copy(dest, src):dest,src的类型都是va_list,va_copy()用于复制参数列表
指针,将dest初始化为src。

va_end(arg_ptr):清空参数列表,并置参数指针arg_ptr无效。说明:指针arg_pt
r被置无效后,可以通过调用va_start()、va_copy()恢复arg_ptr。每次调用va_st
art() / va_copy()后,必须得有相应的va_end()与之匹配。参数指针可以在参数列
表中随意地来回移动,但必须在va_start() … va_end()之内。

【转载】C语言 构建参数个数不固定函数的更多相关文章

  1. C++省略号类型和参数个数不确定函数参数范例

    声明:所有权利保留. 转载必须说明出处:http://blog.csdn.net/cartzhang/article/details/44203651 今天想写个宏定义,发现宏定义里也可以写不定参数, ...

  2. Java 参数个数可变的函数

    示例: package my_package; public class Test { public static void main(String[] args) { out("重庆师范大 ...

  3. C语言可变参数个数

    #include <stdio.h>#include <stdarg.h> void test(const char * format, ...); int main(void ...

  4. c++ 参数个数可变的函数

    #include <stdio.h> #include <string.h> #include <stdarg.h> int addnum(int i,...) { ...

  5. 深入C语言可变参数(va_arg,va_list,va_start,va_end,_INTSIZEOF)

    一.什么是可变参数 在C语言编程中有时会遇到一些参数个数可变的函数,例如printf(),scanf()函数,其函数原型为: int printf(const char* format,…),int ...

  6. C语言可变参数va_list

    一.什么是可变参数 在C语言编程中有时会遇到一些参数个数可变的函数,例如printf(),scanf()函数,其函数原型为: int printf(const char* format,-) int ...

  7. [转载]C/C++可变参数之va_start和va_end使用详解

    本文主要介绍va_start和va_end的使用及原理. 在以前的一篇帖子Format MessageBox 详解中曾使用到va_start和va_end这两个宏,但对它们也只是泛泛的了解. 介绍这两 ...

  8. c++11之获取模板函数的参数个数和函数返回值类型

    本文演示c++需要支持c++11及以上标准 获取参数个数 1.模板函数声明 template <class R, class... Args> R getRetValue(R(*)(Arg ...

  9. Shell脚本中判断输入参数个数的方法投稿:junjie 字体:[增加 减小] 类型:转载

    Shell脚本中判断输入参数个数的方法 投稿:junjie 字体:[增加 减小] 类型:转载   这篇文章主要介绍了Shell脚本中判断输入参数个数的方法,使用内置变量$#即可实现判断输入了多少个参数 ...

随机推荐

  1. mybatis入门基础----高级映射(一对一,一对多,多对多)

    阅读目录 一:订单商品数据模型 二.一对一查询 三.一对多查询 四.多对多查询 回到顶部 一:订单商品数据模型 1.数据库执行脚本 创建数据库表代码: CREATE TABLE items ( id ...

  2. 在html5 canvas的destination-atop属性的一些奇怪的问题

    最近在整理canvas的时候发现HTML5 Canvas开发详解一个奇怪的属性解释 目标图形是显示在画布上的位图 而原图形是指要回执在画布上的形状 w3school上面是这样说的 destinatio ...

  3. 采用jacob实现word转pdf

    网络上已经有很多这方面的内容,在用之前也是参考了好多别人的文章,下面记录下我自己的整合过程.整个过程都比较简单: 开发环境:win8 64位系统,在2008下面部署也是一样的. 文档要求jdk的版本要 ...

  4. python內建模块之datetime

    from:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143193755 ...

  5. 002_分布式搜索引擎Elasticsearch的查询与过滤

    一.写入 先来一个简单的官方例子,插入的参数为-XPUT,插入一条记录. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 curl -XPUT 'http:/ ...

  6. @PostContruct注解

    @PostContruct是spring框架的注解,在方法上加该注解会在项目启动的时候执行该方法,也可以理解为在spring容器初始化的时候执行该方法.

  7. ASP.NET结合COM组件发送Email

    在开发电子邮件发送程序的时候,我们经常需要使用到相应的组件,其实不需要第三方的组件(例如:Jmail)照常可以做到发送Email的功能. 在系统目录(如c:/winnt或c:/windows)的sys ...

  8. 【转载】webstorm忽略node_modules目录

    转载自:http://www.cnblogs.com/chengwb/p/6183440.html 我在使用了cnpm后node_modules之前的层级目录变成了同一级目录,所以目录很多,造成web ...

  9. Database Course Summary 001

    0x01. 基本概念 SQL:Structured English Query Language 1. 数据 Data 数据(Data):描述事物的符号记录:数据内容是事物特性的反应或描述:数据是符号 ...

  10. .NetCore源码阅读笔记系列之Security (二) 自定义认证实践

    通过前面对AddCookie 或者 AddOpenIdConnect 等了解,其实里面都实现了一个AuthenticationHandler<TOptions>的认证处理,接下来我们来简单 ...