使用NULL目标指针调用vsnprintf返回意外值的问题排查
Let's break down the two key issues causing that mismatched m = 10 value instead of the expected 7:
1. You're Reusing a Consumed va_list
Here's the critical problem: when you initialize a va_list with va_start(args, format), it points to the first variable argument in your function call. Functions like vsnprintf advance this pointer as they process each argument. Once you've run vsnprintf once with args, that list is used up—it no longer points to the start of your input arguments.
When you call vsnprintf a second time with the same args without resetting it, you're reading memory beyond your actual input values. This is undefined behavior, which explains the incorrect length value you're seeing.
To fix this, you have two straightforward options:
- Use
va_copyto create a duplicate of the originalva_listbefore your firstvsnprintfcall, then use the copy for the second formatting step. - Or, call
va_end(args)after the firstvsnprintf, then re-runva_start(args, format)to reset the list before the second call.
2. You're Using an Undefined Variable n
Looking at your SAFE_snprintf code, you reference a variable n that doesn't exist in the function's scope. This is a compile error (unless you have an unseen global n), and you clearly meant to use the m value returned by the first vsnprintf call.
Also, a critical memory allocation mistake: vsnprintf returns the number of characters that would be written excluding the null terminator. So when allocating memory, you need m + 1 bytes (not n-1) to fit the full string plus the required null byte. Using m-1 would truncate your string, defeating the purpose of a "safe" snprintf.
Fixed SAFE_snprintf Implementation
Here's a corrected version using va_copy to handle variable arguments properly, plus fixes for the memory allocation and variable issues:
#define long_string 256 typedef char STRING_VARIABLE [long_string + 1]; void SAFE_snprintf(char *buffer, const char *format, ...) { va_list args, args_copy; va_start(args, format); va_copy(args_copy, args); // Make a copy of the original argument list // Calculate the required buffer length int m = vsnprintf(NULL, 0, format, args); va_end(args); // We're done with the original list for now printf("m = %d\n", m); if (m < 0) { perror("vsnprintf failed"); abort(); } // Allocate enough memory (plus 1 for the null terminator) char *bufferString = (char *)malloc(m + 1); if (!bufferString) { perror("malloc failed"); abort(); } // Use the copied list to do the actual formatting m = vsnprintf(bufferString, m + 1, format, args_copy); va_end(args_copy); if (m < 0) { perror("vsnprintf failed"); free(bufferString); // Don't leak memory if formatting fails abort(); } // Important: Add a check here if `buffer` has a fixed size! // Right now, if the formatted string is longer than long_string, strcpy will overflow strcpy(buffer, bufferString); free(bufferString); } int main(int argc, char *argv[]) { char InputString[] = "Hello"; STRING_VARIABLE bufferStrings; const char format[] = "%s_test"; int n = snprintf(NULL, 0, format, InputString); if (n < 0) { perror("snprintf failed"); abort(); } printf("n = %d\n", n); SAFE_snprintf(bufferStrings, format, InputString); printf("Formatted result: %s\n", bufferStrings); // Verify the output return 0; }
Extra Tips for Safety
- Mark your format string as
const char *to enforce type safety (standard practice for functions accepting format strings). - Add a buffer size parameter to
SAFE_snprintfand usestrncpyinstead ofstrcpyto prevent overflowing the input buffer. Right now, if your formatted string exceedslong_string, you'll get a buffer overflow. - Always clean up allocated memory (like
bufferString) if an error occurs after allocation to avoid memory leaks.
内容的提问来源于stack exchange,提问作者Muhammed Mahmood

