va_list misbehavior on Linux
我有一些代码将可变参数转换为
在以下代码示例中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | #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 ", result); printf("%s ", 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 ", result); printf("%s ", 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! ", result); } else { printf("Test succeeded. "); } return 0; } |
第二个
我已经看到但未解决该主题的相关主题:
- 传递va_list或指向va_list的指针?
- 将一个va_list作为参数传递给另一个
使用@Mat的答案(我正在重用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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 ", result); printf("%s ", final); return final; } |
根据C99规范(第7.15节中的脚注),应该可以运行以下命令:
It is permitted to create a pointer to a va_list and pass that pointer
to another function, in which case the original function may make
further use of the original list after the other function returns.
但是我的编译器(在C99模式下为gcc 4.4.5)给我有关
1 2 | test.c: In function ‘myPrintfInner’: test.c:8: warning: initialization from incompatible pointer type |
生成的二进制文件产生与第一次相同的效果。
发现了这一点:GCC是否对传递给函数的va_list的指针进行了错误处理?
建议的解决方法(虽然不能保证能正常工作,但实际上是可以做到的)是首先使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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 ", result); printf("%s ", final); return final; } |
正如Mat所指出的那样,问题在于您正在重用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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 ", result); printf("%s ", final); return final; } |
在不支持C99的编译器上,您可以改用
问题是(除了缺少的return语句之外)您正在重新使用
尝试类似的东西:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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 ", result); printf("%s ", 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; } |
(并打开编译器的警告。)
我认为您指出的脚注并不意味着您认为的含义。我将其读取为:如果直接传递
您可以尝试使用
1 2 3 4 5 6 | char *myPrintfInner(const char *message, va_list params) { va_list temp; va_copy(temp, params); size_t length = vsnprintf(NULL, 0, message, temp); ... |