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…
}