C 我可以使用“va_list”两次吗

C 我可以使用“va_list”两次吗,c,C,我可以按如下方式使用va_列表: void myself_printf(char* key, char* value, ...) { char real_key[1024]; char real_value[1024]; va_list args; va_start(args, value); // use args twice format key and value vsnprintf(real_key, 1024000, key, args

我可以按如下方式使用
va_列表

void myself_printf(char* key, char* value, ...) {
    char real_key[1024];
    char real_value[1024];
    va_list args;
    va_start(args, value);

    // use args twice format key and value
    vsnprintf(real_key, 1024000, key, args);
    vsnprintf(real_value, 1024000, value, args);

    va_end(args);
}
在演示中使用

myself_printf("%d-%s", "%s-%d", 12, "key", "value", 24);
expect: real_key is "12-key", and real_value is "value-24"

严格来说,您必须使用
va_copy
,因为
vsnprintf
可以使
args
无效

void myself_printf(char* key, char* value, ...) {
    char real_key[1024000];
    char real_value[1024000];
    va_list args, args2;
    va_start(args, value);
    va_copy(args2, args);

    // use args twice format key and value
    vsnprintf(real_key, 1024000, key, args);
    vsnprintf(real_value, 1024000, value, args2);

    va_end(args);
    va_end(args2);
}
这就是
va_copy
的设计目的

如前所述,这是一个很大的堆栈空间,尽管它在典型的堆栈大小内。如果可用,请考虑使用<代码> VasPrpTf<代码>。< /P> 引证 n1548§7.16

对象ap可以作为参数传递给另一个函数;如果该函数使用参数ap调用va_arg宏,则调用函数中的ap值是不确定的

换句话说,在将
args
传递给
vsnprintf
后,您无法将其移植使用

脚注281对此进行了澄清:

由于函数vfprintf、vfscanf、vprintf、vscanf、vsnprintf、vsprintf和vsscanf调用va_arg宏,因此返回后的arg值是不确定的

虽然
va_list
似乎是按值传递的,但这并不意味着它实际上是按值传递的,或者
va_list
本身封装了它的所有状态。C中typedefs的一个常见技巧是将它们声明为1元素数组:

typedef int my_type[1];
由于
my_type
在通过函数传递时衰减为指针类型,因此它似乎只通过值传递

演示
#包括
#包括
无效函数(常量字符*消息,…){
va_列表ap;
va_启动(ap、msg);
vfprintf(stdout、msg、ap);
vfprintf(stdout、msg、ap);
va_端(ap);
}
int main(int argc,字符**argv){
func(“%d+%d=%d\n”,2,3,5);
返回0;
}
在我的计算机上,输出为:

2 + 3 = 5 590862432 + -1635853408 = 1586038440 2 + 3 = 5 590862432 + -1635853408 = 1586038440
作为使用
va_copy()
的替代方法-如中所建议的-您只需使用
va_start()
va_end()
两次即可

void myself_printf(char *key_fmt, char *value_fmt, ...)
{
    char real_key[1024];
    char real_value[1024];
    va_list args;

    va_start(args, value);
    vsnprintf(real_key, sizeof(real_key), key_fmt, args);
    va_end(args);

    vs_start(args, value);
    vsnprintf(real_value, sizeof(real_key), value_fmt, args);
    va_end(args);

    …do something useful with real_key and real_value…
}
问题的原始版本使用了char real_key[1024000]和类似的
实际值
。在堆栈上分配几乎2个MiB的数据在Windows上无法可靠工作(限制通常是1个MiB的堆栈),并且在Unix系统上会占用大量空间(其中堆栈大小通常为8个MiB)。小心

如果将
va_列表
作为参数传递给函数,则需要使用
va_copy()
。例如:

void myself_printf(char* key, char* value, ...)
{
    va_list args;    
    va_start(args, value);
    myself_vprintf(key, value, args);
    va_end(args);
}

void myself_vprintf(char *key_fmt, char *value_fmt, va_list args1)
{
    char real_key[1024];
    char real_value[1024];
    va_list args2;
    va_copy(args2, args1);

    vsnprintf(real_key, sizeof(real_key), key_fmt, args1);
    vsnprintf(real_value, sizeof(real_value), value_fmt, args2);
    va_end(args2);

    …do something useful with real_key and real_value…
}
本规范规定:

va_end
宏有助于从其变量参数列表被
va_start
宏的展开引用的函数,或包含初始化
va_list ap
va_copy
宏的展开的函数正常返回。
va_end
宏可以修改
ap
,使其不再可用(无需通过
va_start
va_copy
宏重新初始化)。如果没有相应调用
va_start
va_copy
宏,或者在返回之前没有调用
va_end
宏,则行为未定义

请注意,函数的说明包括脚注,其中说明:

由于函数
vfprintf
vfscanf
vprintf
vscanf
vsnprintf
、和
vsscanf
调用
va_arg
宏,返回后
arg
的值是不确定的


诚然,脚注不是规范性的,但这是一个强烈的迹象,表明预期函数将使用
va_arg
,因此问题中所示的
arg
的双重使用会导致未定义的行为,除非有对
va_end
的中间调用和另一个
va_start
或使用
va_copy
(及其匹配的
va_end

你确定你是在问C++吗?很显然你可以。但是,与这个问题无关,在栈上分配那些大的缓冲区是导致问题的。堆栈大小在大多数编译器上默认是1MB,你需要2MB来分配<代码> RealOyValue< /C>和<代码> RealYKEY < /代码>。操作可能会导致崩溃。您必须减小这些缓冲区,或者使用
malloc()
@havenard incorrect将它们分配到堆上不正确,只是不完整。1MB默认堆栈大小对于VS2017是正确的,但对于Linux-
ulimit-s
。您确定吗?我不这样认为(但可能是错误的).OP使用了两次
args
值,而将其传递给
vsnprintf
的AFAIK并没有使
args
值无效。但我可能是错的。的示例与您的代码不同。调用
vsnprintf
时,
args
是按值传递的。@basilestrynkevitch:是的,非常确定。添加了引文。但调用
va_copy
(不是使用它的代码)不是
vsnprintf
实现的角色吗?没有指定
vsnprintf
正在使用
va_arg
@BasileStarynkevitch:不管
vsnprintf
是否可以使用其他东西,因为事实上,常见的实现确实调用
va_arg
而不调用
va_copy
@BasileStarynkevitch:请试用example code。在我的系统(x86-64 Linux ELF)上,第二个vfprintf调用的输出是垃圾。Linux和Linux的最新链接,
ulimit-s
。两者都死机了。
void myself_printf(char* key, char* value, ...)
{
    va_list args;    
    va_start(args, value);
    myself_vprintf(key, value, args);
    va_end(args);
}

void myself_vprintf(char *key_fmt, char *value_fmt, va_list args1)
{
    char real_key[1024];
    char real_value[1024];
    va_list args2;
    va_copy(args2, args1);

    vsnprintf(real_key, sizeof(real_key), key_fmt, args1);
    vsnprintf(real_value, sizeof(real_value), value_fmt, args2);
    va_end(args2);

    …do something useful with real_key and real_value…
}