在 C++ 中传递 va_list 作为函数的引用是否合法?

问题描述 投票:0回答:1

在代码审查/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 本身作为参考传递。哦,好吧。

c++ pass-by-reference variadic-functions
1个回答
0
投票

是的,这是允许的。首先,[cstdarg.syn] 指出:

头文件内容与C标准库头文件相同,有以下变化:

  • [...]

这里的任何限制都不允许形成对

std::va_list
的引用。

C标准中的相关措辞

答案在于C17标准,7.16变量参数,p3

声明的类型是

va_list

这是一个完整的对象类型,适合保存所需的信息 宏

va_start
va_arg
va_end
va_copy
。如果需要访问不同的参数,则调用的函数 应声明一个具有类型
ap
的对象(在本子条款中通常称为
va_list
)。 对象
ap
可以作为参数传递给另一个函数;如果该函数调用
va_arg
宏 使用参数
ap
,调用函数中
ap
的值是不确定的,应传递给
va_end
宏先于进一步引用
ap
257)

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
变得不确定。

© www.soinside.com 2019 - 2024. All rights reserved.