C snprintf Return Values: Detect Truncation Before Using the Length
Distinguish required length from stored bytes in C snprintf. Test eight boundary scenarios and reject truncated output before using it as a transmission length, including UTF-8 cases.
C's snprintf returns the length that would have been needed with enough space, not necessarily the length stored in the buffer. When building logs or protocol strings in fixed buffers, check for a negative error first, then treat a return value at least as large as the capacity as truncation. The example below reproduces this difference and UTF-8 truncation, and assigns a usable transmission length only to complete strings.
A long cyan tile ribbon and a short transparent tray illustrate required output length versus buffer capacity. This is a conceptual illustration, not an actual memory layout.
Written and documentation checked: 2026-10-31. Execution environment: Linux amd64, GCC 13.3.0, glibc 2.39. This article covers standard snprintf; these are not execution results for the Windows CRT.
A size limit does not make every length safe to use
Writing "ABCDEFG" into char buf[5] with snprintf stores "ABCD" and a null terminator in the tested environment, but returns 7. Passing that value directly as the length to a transmission API such as send(fd, buf, n, ...) can make it read beyond the buffer. The incorrect transmission call is not executed in this article.
The GNU C Library manual explains the required length excluding the terminator and its comparison with capacity. Lengths for this char buffer are measured in bytes, not visible characters.
Handle the return value in three branches
| Check order | Meaning | Example policy |
n < 0 | Formatting error | Fail, length 0 |
n >= 0 and (size_t)n >= cap | Not enough space including the terminator | Reject truncation, length 0 |
| Otherwise | The complete result and terminator fit | Return length n |
Converting to size_t before checking for a negative result turns an error into a large positive value. n == cap is not success either: the final terminator needs one more byte.
This example adopts a no-truncation policy. On failure, it clears the buffer's first byte when capacity is available and keeps the reported length at zero. Callers must use the result only when the status is FORMAT_OK, rather than checking the length alone.
Runnable code: report a length only for a complete result
Save this as snprintf_demo.c. Input must be a valid null-terminated C string that does not overlap the destination. The destination must provide at least cap bytes, and may be NULL only when cap == 0. This is not a general string library that validates ownership or input validity.
The capacity obtained with sizeof applies to a local array. Applying sizeof to a char * function parameter does not give the buffer capacity, so pass capacity separately. The example's len excludes the terminator. Actual socket transmission still requires handling partial writes and errors.
Verification commands and actual results
GCC emitted -Wformat-truncation warnings for the two deliberately truncated raw calls, consistent with the behavior being reproduced. Execution finished with exit code 0 and no AddressSanitizer or UndefinedBehaviorSanitizer diagnostics. LeakSanitizer was disabled because of process-access restrictions in the execution environment. This example performs no dynamic allocation.
| Eight boundary scenarios | Observation |
| Raw call: capacity 5, seven-byte input | Required length 7, stored length 4 |
| Wrapper: capacity 8, seven-byte input | Success, length 7 |
| Wrapper: capacity 7, seven-byte input | Reject: no room for the terminator |
| Wrapper: capacity 1, input A | Reject truncation |
| Wrapper: capacity 1, empty input | Successful empty string |
| Wrapper: capacity 0, input A | Original memory unchanged; reject |
| Wrapper: NULL, capacity 0, empty input | Reject: no storable result |
| Three-byte UTF-8 input, capacity 3 | Raw call leaves two bytes; wrapper rejects |
The last scenario uses EA B0 80, the UTF-8 bytes for U+AC00, the Korean syllable ‘가’. The raw result EA B0 lacks its third byte and is not a complete UTF-8 character, as can be checked against the byte structure in RFC 3629 §3. A null-terminated C string is not necessarily valid text in its intended encoding.
Decide the policy before applying the wrapper
- Reject truncation or regenerate with a larger buffer for values whose meaning changes when shortened, such as TTS input, JSON, or authentication data. Clamping the length with
min(n, cap - 1)may avoid some bounds problems but does not establish content completeness. Subtraction whencap == 0also needs care.
- If a UI preview permits shortening, define a separate policy for UTF-8 boundaries and user-visible character boundaries. This wrapper is not a UTF-8 validator and does not filter invalid encoding that fits without truncation.
- The negative-error branch is included, but no real formatting error was forced in this fixed
"%s"example. Invalid pointers, overlapping buffers, and mismatched argument types are not test inputs from which safe error returns can be expected.
- Microsoft's documentation distinguishes UCRT's standard
snprintffrom legacy_snprintfin truncation return values and null termination. Do not substitute one name for the other indiscriminately when porting Windows code.