乐趣区

关于c++:C可变参数函数实现方法

C++ 可变参数函数实现办法

C++ 编程中实现可变参数函数有多种路径,本文介绍一种最常见的实现路径,即可变参数宏办法:形参生命为省略符,函数实现时用参数列表宏拜访参数。

1. 可变参数宏实现变参函数

可变参数宏实现可分为以下几个步骤:

  1. 函数形参原型中给出省略符;
  2. 函数实现中申明一个 va_list 可变参数列表变量;
  3. 开始初始化结构 va_list 变量;
  4. 拜访变参列表;
  5. 实现清理工作;

上述步骤的实现须要应用到四个宏:

  1. va_list
  2. void va_start(va_list ap, last_arg)
  3. type va_arg (va_list ap, type)
  4. void va_end(va_list ap)
va_list 是在 C 语言中解决变参问题的一组宏 
void va_start(va_list ap, last_arg)
ap:是一个 va_list 类型的对象,它用来存储通过 va_arg 获取额定参数时所必须的信息。last_arg:是最初一个传递给函数的已知的固定参数,即省略号之前的参数。
 宏定义:type va_arg (va_list ap, type)
该宏用于变参数函数调用过程中,type 是以后参数类型,调用该宏后,ap 指向变参数列表中的下一个参数,返回 ap 指向的参数值,是一个类型为 type 的表达式。ap 是 arg_ptr 参数指针之意。
void va_end(va_list ap)
容许应用了 va_start 宏的带有可变参数的函数返回。如果在从函数返回之前没有调用 va_end,则后果为未定义。

这些宏在头文件 stdarg.h 中申明定义。因而应用时须要蕴含该头文件。

上面给出用法示例:

#include <stdarg.h>

// 可变参数函数 sum(), 求任意个数整数的和。//Step1: 函数形参原型中给出省略符
int Sum(int count, ...);

int Sum(int count, ...) {
    //Step2:  函数实现中申明一个 va_list 可变参数列表变量;va_list ap;
    //Step3: 开始初始化结构 va_list 变量, 第二个参数为最初一个确定的形参
    va_start(ap, count);
    int sum = 0;
    for(int i = 0; i < count; i++) {
        // 读取可变参数,的二个参数为可变参数的类型
        sum += va_arg(ap, int);
    }
    // 清理工作
    va_end(ap);
    return sum;
}

理论中应用可变参数宏实现 C ++ 可变参数函数编程,还要留神一下几点:

  • 函数原型中省略号必须在参数列表的开端:也就是说,在函数原型中参数列表省略号的左边不能再呈现确定参数;
  • 试用实现是用 va_end 做清理工作步骤不可短少,否则可能导致内存或资源透露;
  • va_list 在一次拜访中不能后退,但能够屡次结构 va_list 屡次拜访;

2. 更平安的可变参数函数实现办法

对于下面示例代码中 count 传进的实参如果与前面 … 省略符对应的理论参数数量不统一时,可能导致函数危险。这所有齐全依赖运行时的具体情况而定,很不平安。

另一种更平安的可变参数宏实现办法是利用 C ++ 的 attribute ((format())) 个性来辅助可变参数的查看。

最常见的模式是有如下两个:

__attribute__((format(printf, m, n)))  
__attribute__((format(scanf, m, n)))

其中参数 m 与 n 的含意为:
m:第几个参数为格式化字符串 (format string);
n:参数汇合中的第一个,即参数“…”里的第一个参数在函数参数总数排在第几;

attribute format 属性能够给被申明的函数加上相似 printf 或者 scanf 的特色,它能够使编译器查看函数申明和函数理论调用参数之间的格式化字符串是否匹配。format 属性通知编译器,依照 printf, scanf 等规范 C 函数参数格局规定对该函数的参数进行查看。这在咱们本人封装调试信息的接口时十分的有用。

format 的语法格局为:

format (archetype, string-index, first-to-check)
  其中,“archetype”指定是哪种格调;“string-index”指定传入函数的第几个参数是格式化字符串;“first-to-check”指定从函数的第几个参数开始按上述规定进行查看。

上面给出 2 个示例:

个别函数:

为本人定义的一个带有可变参数的函数,其性能相似于 printf:
extern void myprint(const char *format,…) attribute ((format(printf,1,2))); //m=1;n=2
extern void myprint(int l,const char *format,…) attribute ((format(printf,2,3))); //m=2;n=3

类成员函数

须要特地留神的是,如果 myprint 是一个函数的成员函数,那么 m 和 n 的值可有点“悬乎”了,例如:
extern void myprint(int l,const char *format,…) attribute ((format(printf,3,4)));

其起因是,类成员函数的第一个参数实际上一个隐身的 this 指针。

退出移动版