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

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. 《高性能MySQL》——第一章MySQL的架构与历史

    1.可以使用SHOW TABLE STATUS查询表的相关信息. 2.默认存储引擎是InnoDB,如果没有什么很特殊的要求,InnoDB引擎是我们最好的选择. 3.mysql的infobright引擎 ...

  2. JavaScript 获取 flash 对象

    关于js获取flash对象,网上有非常多的例子,我也尝试了不少方法. 虽然都能用,但是没有我最想要的东西, 后来看了下百度的,虽然很规范,各种情况都考虑到了,但是代码量却不是不容乐观, 前前后后将近2 ...

  3. 20155202 2016-2017-2 《Java程序设计》第5周学习总结

    20155202 2016-2017-2 <Java程序设计>第5周学习总结 教材学习内容总结 第八章:异常处理 java中所有错误会包装成对象,可以尝试(try)执行程序并捕捉(catc ...

  4. DDLog设置方法

          CHENYILONG Blog DDLog设置方法 本文永久地址为http://www.cnblogs.com/ChenYilong/p/3984246.html,转载请注明出处. 201 ...

  5. HDU 2054 又见GCD

    又见GCD Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Subm ...

  6. 判断gps是否在国内

    参考文章:[WP7]判断GPS坐标是否在中国 根据国家行政边界判定(光线投射算法) 按需求调整了原文中的部分边界值,测试几组边界附近内外坐标,结果较为准确. /** * 判断GPS坐标是否在多边形中 ...

  7. 论参数self

    此篇文章仅适用于py3.在py2中,a.fuc(x)中的参数x必须是 类a的实例对象,而py3则可以是任意对象.参考绑定方法和非绑定方法 当一个对象添加了一个方法,并且此方法的第一个参数为self,或 ...

  8. TensorFlow 从零到helloWorld

    目录 1.git安装与使用 1.1 git安装 1.2 修改git bash默认路径 1.3 git常用操作 2.环境搭建   2.1 tensorflow安装   2.2 CUDA安装   2.3 ...

  9. pymongo创建索引

    from database import db db_list = ["table1", "table2", "table3", " ...

  10. C# 百度搜索结果xpath分析

    using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net ...