I have code that converts variable parameters to va_list and then passes the list to a function that then calls vsnprintf . This works fine on Windows and OS X, but on Linux it doesn't work with odd results.
In the following code example:
#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; }
The output of the second vsnprintf call is 17, and the result of strcmp is 31; but I donโt understand why vsnprintf will return 17 after seeing This is a test. is 15 characters, add NULL and you get 16.
Related topics that I saw but not addressing the topic:
- Skip va_list or pointer to va_list?
- Passing one va_list as a parameter to another
With @Mat's answer (I am reusing the va_list object, which is invalid), this happens right around the first associated stream that I am associated with. So I tried using this code:
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; }
What, for the C99 specification (footnote in section 7.15), should work:
It is allowed to create a pointer to va_list and pass this pointer to another function, in which case the original function can continue to use the original list after returning another function.
But my compiler (gcc 4.4.5 in C99 mode) gives me this error regarding the first line of myPrintfInner :
test.c: In function 'myPrintfInner': test.c:8: warning: initialization from incompatible pointer type
And the resulting binary produces the same effect as the first time.
Found: Is the GCC pointer to va_list passed to the function incorrectly?
The proposed workaround (which is not guaranteed to work, but in practice) is to use arg_copy first:
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; }