我有一些代码可以将可变参数转换为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;
}
答案 0 :(得分:12)
Mat注意到,问题是你正在重用va_list
。如果您不想按照他的建议重新构建代码,可以使用C99 va_copy()
宏,如下所示:
char *myPrintfInner(const char *message, va_list params)
{
va_list copy;
va_copy(copy, params);
size_t length = vsnprintf(NULL, 0, message, copy);
va_end(copy);
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;
}
在不支持C99的编译器上,您可以use __va_copy()
instead or define your own va_copy()
implementation(这将是不可移植的,但如果您确实需要,您可以始终在头文件中使用编译器/平台嗅探)。但实际上,它已经有13年了 - 现在任何体面的编译器都应该支持C99,至少如果你给它选择正确的选项(GCC的-std=c99
)。
答案 1 :(得分:7)
问题是(除了丢失的return语句之外)你重新使用va_list
参数而不重置它。那不好。
尝试类似:
size_t myPrintfInnerLen(const char *message, va_list params)
{
return vsnprintf(NULL, 0, message, params);
}
char *myPrintfInner(size_t length, const char *message, va_list params)
{
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 = myPrintfInnerLen(message, va_args);
va_end(va_args);
va_start(va_args, message);
char *ret = myPrintfInner(length, message, va_args);
va_end(va_args);
return ret;
}
(并打开编译器的警告。)
我不认为你指的脚注意味着你的想法。我把它读作:如果直接传递va_list
(作为值,而不是指针),你在调用者中唯一能做的就是va_end
它。但是如果你把它作为一个指针传递,你可以,如果被调用者没有“消耗”所有va_arg
,你可以在调用者中调用va_list
。
你可以试试va_copy
。类似的东西:
char *myPrintfInner(const char *message, va_list params)
{
va_list temp;
va_copy(temp, params);
size_t length = vsnprintf(NULL, 0, message, temp);
...