【发布时间】:2012-03-30 05:37:09
【问题描述】:
我有一些代码可以将可变参数转换为va_list,然后将列表传递给调用vsnprintf 的函数。这在 Windows 和 OS X 上运行良好,但在 Linux 上失败并出现奇怪的结果。
在以下代码示例中:
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
char *myPrintfInner(const char *message, va_list params)
{
va_list *original = ¶ms;
size_t length = vsnprintf(NULL, 0, message, *original);
char *final = (char *) malloc((length + 1) * sizeof(char));
int result = vsnprintf(final, length + 1, message, params);
printf("vsnprintf result: %d\r\n", result);
printf("%s\r\n", final);
return final;
}
char *myPrintf(const char *message, ...)
{
va_list va_args;
va_start(va_args, message);
size_t length = vsnprintf(NULL, 0, message, va_args);
char *final = (char *) malloc((length + 1) * sizeof(char));
int result = vsnprintf(final, length + 1, message, va_args);
printf("vsnprintf result: %d\r\n", result);
printf("%s\r\n", final);
va_end(va_args);
return final;
}
int main(int argc, char **argv)
{
char *test = myPrintf("This is a %s.", "test");
char *actual = "This is a test.";
int result = strcmp(test, actual);
if (result != 0)
{
printf("%d: Test failure!\r\n", result);
}
else
{
printf("Test succeeded.\r\n");
}
return 0;
}
第二次vsnprintf调用的输出是17,strcmp的结果是31;但我不明白为什么 vsnprintf 会返回 17,因为 This is a test. 是 15 个字符,添加 NULL 会得到 16。
我看过但未涉及该主题的相关主题:
有了@Mat 的回答(我正在重用va_list 对象,这是不允许的),这正好与我链接到的第一个相关线程有关。所以我尝试了这段代码:
char *myPrintfInner(const char *message, va_list params)
{
va_list *original = ¶ms;
size_t length = vsnprintf(NULL, 0, message, params);
char *final = (char *) malloc((length + 1) * sizeof(char));
int result = vsnprintf(final, length + 1, message, *original);
printf("vsnprintf result: %d\r\n", result);
printf("%s\r\n", final);
return final;
}
per the C99 spec(第 7.15 节中的脚注)应该可以工作:
允许创建指向 va_list 的指针并传递该指针 到另一个函数,在这种情况下,原始函数可能会使 在其他函数返回后进一步使用原始列表。
但是我的编译器(C99 模式下的 gcc 4.4.5)给了我这个关于 myPrintfInner 第一行的错误:
test.c: In function ‘myPrintfInner’:
test.c:8: warning: initialization from incompatible pointer type
生成的二进制文件会产生与第一次完全相同的效果。
找到这个:Is GCC mishandling a pointer to a va_list passed to a function?
建议的解决方法(不能保证有效,但在实践中确实有效)是首先使用arg_copy:
char *myPrintfInner(const char *message, va_list params)
{
va_list args_copy;
va_copy(args_copy, params);
size_t length = vsnprintf(NULL, 0, message, params);
char *final = (char *) malloc((length + 1) * sizeof(char));
int result = vsnprintf(final, length + 1, message, args_copy);
printf("vsnprintf result: %d\r\n", result);
printf("%s\r\n", final);
return final;
}
【问题讨论】:
-
您的
myPrintf函数缺少return语句。我希望你的编译器会警告你。 -
呸,骗子!复制粘贴失败。
-
您的新代码与旧代码做的事情完全相同:
original指向params,因此传递*original与传递params完全相同。您真正的问题似乎是您不了解va_lists 的工作原理:它们本质上是指向参数堆栈的指针,并且指针在使用时会递增。因此,当您使用相同的va_list两次时,第二次您将指针递增到参数列表的末尾。
标签: c variadic-functions