在代码审查/clang-tidy 运行时,我遇到了一个具有如下签名的函数:
void appendFoo(const char * fmt, va_list& rVaList);
我以前从未见过这个。 Afaik,您可以按值传递 va_list (也可以通过指针传递?)。
这让我想到了我的第一个问题:通过引用传递
va_list
是否合法并且它是否按预期工作?
无论如何,
appendFoo()
在其定义中调用了vsprintf()
,并且 clang-tidy 给出了以下警告:Function 'vsprintf' is called with an uninitialized va_list argument [clang-analyzer-valist.Uninitialized]
。 appendFoo()
的定义基本上是这样的(请原谅删节:( ):
void appendFoo(const char * fmt, va_list& rVaList) {
// retracted: allocate buffer
vsprintf(buffer, fmt, rVaList);
// errorhandling
// de-allocate buffer
}
(是的,vsprintf 的返回值被忽略,错误以另一种方式“处理”。我正在修复它......)
特别是没有调用va_copy、va_start等。
删除引用并按值传递 va_list,即将签名更改为
void appendFoo(const char * fmt, va_list rVaList);
,消除 clang-tidy 警告。
这引出了我的第二个问题:如果 va_list 通过引用传递,clang-tidy 产生的警告是否为误报,或者在传递 va_list 作为引用时实际上存在问题?
有人可以澄清一下吗?
PS:这个问题不是重复的varargs(va_list va_start)不适用于传递引用参数。该问题的OP通过引用采用va_list的函数来传递参数。我问的是 va_list 本身作为参考传递。哦,好吧。
是的,这是允许的。首先,[cstdarg.syn] 指出:
头文件内容与C标准库头文件相同
,有以下变化:
- [...]
这里的任何限制都不允许形成对
std::va_list
的引用。
答案在于C17标准,7.16变量参数
声明的类型是
va_list
这是一个完整的对象类型,适合保存所需的信息 宏
、va_start
、va_arg
和va_end
。如果需要访问不同的参数,则调用的函数 应声明一个具有类型va_copy
的对象(在本子条款中通常称为ap
)。 对象va_list
可以作为参数传递给另一个函数;如果该函数调用ap
宏 使用参数va_arg
,调用函数中ap
的值是不确定的,应传递给ap
宏先于进一步引用va_end
。257)ap
257) 允许创建一个指向
的指针并将该指针传递给另一个函数,在这种情况下,原始函数可以在另一个函数返回后进一步使用原始列表。va_list
本质上,通过指针传递
std::va_list
允许被调用者使用 std::va_list
,就像我们在自己的函数中使用它一样。
由于 std::va_list
是一个完整的对象类型,因此可以绑定对其的引用。
还值得注意的是,ABI 中的指针和引用是相同的,因此两者都会将内存地址传递给函数,并且行为或多或少相同。
另请参阅:传递 va_list 或指向 va_list 的指针?
va_list
在您的情况下毫无意义但是,在您的示例中使用对
std::va_list
的引用是没有意义的:
void appendFoo(const char * fmt, va_list& rVaList) { vsprintf(buffer, fmt, rVaList); }
问题在于
std::vsprintf
按值接受 std::va_list
,因此当它在内部调用 va_arg
时,std::va_list
调用者中的 appendFoo
变得不确定。
你也可以按价值通过
std::va_list
,这不会更糟。
va_list
才有意义?如果您想继续在函数的调用者中使用列表,这是有意义的:
std::array<int, 3> get_triple(std::va_list& args_ref) {
return {
va_arg(args_ref, double), va_arg(args_ref, double), va_arg(args_ref, double)
};
}
// ...
std::va_list args;
va_start(args, /* ... */);
auto [x, y, z] = get_triple(args);
auto [u, v, w] = get_triple(args);
// ...
如果我们没有通过引用传递
args_ref
,那么在va_arg
中使用get_triple
将使args
变得不确定。