#include<stdarg.h>
int maxof(int n_args, ...)
{
va_list ap;
va_start(ap, n_args);
int max = va_arg(ap, int);
for(int i = 2; i <= n_args; i++) {
int a = va_arg(ap, int);
if(a > max) max = a;
}
va_end(ap);
return max;
}
f(int a)
{int res=a; return res;}
f(int a, int b)
{int res=a+b; return res;}
等等...
16
int fun(int n_args, ...) {
int *p = &n_args;
int s = sizeof(int);
p += s + s - 1;
for(int i = 0; i < n_args; i++) {
printf("A1 %d!\n", *p);
p += 2;
}
}
普通版
8
使用可变参数模板,例如重现 console.log ,如JavaScript中所示:
Console console;
console.log("bunch", "of", "arguments");
console.warn("or some numbers:", 1, 2, 3);
console.error("just a prank", "bro");
15 回答
在C 11中,有一种方法可以使用变量参数模板,这种模板可以实现具有可变参数函数的非常优雅且类型安全的方式 . Bjarne本人在C++11FAQ中给出了printf using variable argument templates的一个很好的例子 .
就个人而言,我认为这很优雅,我甚至不会在C中使用变量参数函数,直到该编译器支持C 11变量参数模板 .
您可能不应该
va_list
获取va_list
类型以及对其进行操作的三个函数,称为va_start()
,va_arg()
和va_end()
.如果你问我,这是一团糟 . 它看起来很糟糕,它充满了技术细节,与你在概念上要实现的目标无关 . 相反,请考虑使用重载或继承/多态,构建器模式(如在流中的
operator<<()
中)或默认参数等 . 这些都更安全:编译器会更多地了解您尝试执行的操作,因此有更多情况可以在你的腿断了之前阻止你 .在C 11中,您有两个新选项,因为Alternatives部分中的Variadic functions参考页面指出:
以下是显示两种备选方案的示例(see it live):
如果您正在使用
gcc
或clang
,我们可以使用PRETTY_FUNCTION magic variable来显示函数的类型签名,这有助于了解正在发生的事情 . 例如使用:将结果int跟随示例中的可变参数函数(see it live):
在Visual Studio中,您可以使用FUNCSIG .
Update Pre C++11
预C 11 std::initializer_list的替代方案将是std::vector或其他standard containers之一:
可变参数模板的替代方案是variadic functions虽然它们不是类型安全的,但通常是error prone and can be unsafe to use但是唯一的另一种可能的替代方法是使用默认参数,尽管使用有限 . 以下示例是链接引用中示例代码的修改版本:
使用可变参数函数时,您可以传递的参数也有限制,详见draft C++ standard部分
5.2.2
函数调用第7段:C支持C风格的可变参数功能 .
但是,大多数C库使用另一种习惯用法,例如而
'c' printf
函数采用变量参数,c++ cout
对象使用<<
重载,它解决了类型安全和ADT(可能以实现简单为代价) .在c 11你可以这样做:
列表初始化FTW!
除了varargs或重载之外,您可以考虑在std :: vector或其他容器(例如std :: map)中聚合您的参数 . 像这样的东西:
通过这种方式,您将获得类型安全性,并且这些可变参数的逻辑含义将是显而易见的 .
当然这种方法可能存在性能问题,但除非您确定无法付出代价,否则不要担心它们 . 这是一种“pythonic”方法来...
唯一的方法是通过使用C样式变量参数,如here所述 . 请注意,这不是推荐的做法,因为它不是类型安全且容易出错的 .
一个C 17解决方案:完整类型安全好的调用语法
自从在C 11中引入可变参数模板和在C 17中引入折叠表达式,可以定义一个模板函数,该函数在被调用者站点可被调用,就好像它是一个varidic函数,但具有以下优点:
是强类型安全的;
没有参数个数的运行时信息,或者没有使用"stop"参数 .
以下是混合参数类型的示例
另一个强制类型匹配所有参数:
更多信息:
Variadic模板,也称为参数包Parameter pack(since C++11) - cppreference.com .
折叠表达式fold expression(since C++17) - cppreference.com .
在coliru上查看full program demonstration .
如果不采用C风格的变种(
...
),没有标准的C方式可以做到这一点 .当然,默认参数可以根据上下文“看起来”像可变数量的参数:
所有四个函数调用都使用不同数量的参数调用
myfunc
. 如果没有给出,则使用默认参数 . 但请注意,您只能省略尾随参数 . 没有办法,例如省略i
并仅给出j
.您可能需要重载或默认参数 - 使用默认参数定义相同的函数:
这将允许您使用以下四种不同的调用之一调用该方法:
...或者你可能正在寻找来自C的v_args调用约定 .
正如其他人所说,C式varargs . 但你也可以用默认参数做类似的事情 .
如果你知道将提供的参数数量范围,你总是可以使用一些函数重载,比如
等等...
普通版
使用可变参数模板,例如重现
console.log
,如JavaScript中所示:文件名,例如
js_console.h
:如果所有参数都是const且类型相同,我们也可以使用initializer_list