Use C99 snprintf for Bounded String Formatting and Truncation Checks
Use snprintf instead of sprintf, pass the real buffer size, and check the return value to catch truncation before trusting a C string.
22 Jul 2025, 09:38 UTC

Problem: sprintf writes formatted text into a buffer with no size limit. A long host name, URL, or user input can overwrite memory past the end of the array, causing a classic buffer overflow. Takeaway: use snprintf from stdio.h, pass the actual array size, and check the return value so truncation is never mistaken for a complete string.
How snprintf bounds output
snprintf is the C99 bounded replacement for sprintf. A buffer is a contiguous block of memory, usually an array, that holds text temporarily. The call writes at most size - 1 characters and always appends a null terminator, the '\0' byte that marks the end of a C string. It returns the number of characters that would have been written if the buffer were large enough, not counting the null terminator.
Because the return value describes the full would-be output, you can detect two failure conditions:
n < 0: an encoding error occurred.(size_t)n >= size: the output was truncated.
Worked example: building an endpoint string
The following function formats host:port into a caller-provided buffer and reports whether the result fits.
#include
#include
int build_endpoint(char *buf, size_t size, const char *host, int port)
{
int n = snprintf(buf, size, "%s:%d", host, port);
if (n < 0) {
return -1; /* encoding error */
}
if ((size_t)n >= size) {
return -2; /* truncated; buffer too small */
}
return 0; /* complete, null-terminated string */
}
Call it with the array's size, not a hard-coded constant:
char endpoint[32];
int status = build_endpoint(endpoint, sizeof(endpoint), "api.example.com", 443);
if (status != 0) {
/* handle: allocate a larger buffer or reject the input */
}
sizeof(endpoint) is 32, so snprintf writes at most 31 characters plus the null terminator. The string api.example.com:443 fits. If host were long enough to require more than 31 characters, n would be greater than or equal to 32 and the function would return -2 instead of pretending the buffer holds the full address.
Zero-size buffer edge case
When size is zero, snprintf writes nothing and buf may be NULL. The return value still reflects the full required length. This supports a two-pass sizing pattern, but confirm the behavior in your target standard library documentation before relying on it for allocation.
Common mistakes
| Mistake | Why it fails |
|---|---|
| Ignoring the return value | The buffer may contain a truncated string, and the program continues as if the output were complete. |
Allocating exactly strlen(result) bytes | Omits space for the terminating null character. |
Passing sizeof(pointer) as size | Yields the pointer width, not the array length. On a 64-bit system that can be 8 instead of the intended buffer size. |
Treating snprintf as a guarantee of complete output | Truncation is silent unless the return value is checked. |
Compile-time and runtime verification
Compile with a C99 or later mode and enable format warnings:
gcc -std=c99 -Wall -Wextra -Wformat -Wformat-truncation -o build_endpoint build_endpoint.c
Run this in an ordinary terminal in the project directory; no special permissions are required. Replace build_endpoint.c with your source file name. -Wformat-truncation can warn when a constant format string always exceeds a fixed buffer, though it does not catch every dynamic case.
At runtime, test three cases with the same function: a buffer that is too small, a buffer that fits exactly, and a buffer that is oversized. For each case, assert the return value and confirm that buf[size - 1] is '\0' when the buffer is non-empty. Do not assume the contents are complete based on the buffer alone.
Limitations
snprintfis not atomic with respect to concurrent writers sharing the same buffer. Separate buffers or external locking is required for multithreaded use.- It does not allocate memory; the caller must provide a buffer large enough for the expected output.
- A negative return value indicates an encoding error, not merely truncation.
Practical check
After a successful call where n < size, the string is null-terminated at buf[n]. When n >= size, the buffer contains size - 1 characters followed by a null terminator at buf[size - 1], but the logical string is incomplete. Use the return-value comparison above as the gate before passing the buffer to any function that expects a complete value.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.