On 8/5/24, Paul Eggert wrote:
If snprintf (buf, sizeof buf, ...) returns an integer I such that (0 <= I < sizeof buf) then the snprintf worked, the contents of buf[0] through buf[I] are valid, and buf[I] == '\0'. Otherwise snprintf didn't work and buf's contents are indeterminate.
In practice, then "... contents are indeterminate" is too pessimistic. Nearly always the contents are a good approximation to what is desired. Often it is necessary to make progress despite imperfect snprintf. Over several decades of porting, maintaining, and developing apps, I used these three techniques to make progress with a large handful of different implementations (and specifications!) of snprintf: 1) Terminate the buffer yourself, anyway: snprintf(buffer, length, format, args...); buffer[-1 + length] = '\0'; This defends against implementations that forget to terminate the buffer on overflow. 2) If the resulting length is needed, then apply strlen() anyway: len1 = snprintf(buffer, length, format, args...); buffer[ -1 + length] = '\0'; len2 = strlen(buffer); This "extra work" will be fast enough because the buffer will be in the cache. You can be fooled by printing a NUL byte ('\0') using a "%c" format specifier. But in 2 of my 3 actual cases, this helped discover a bug in the caller: the argument should not have been a NUL byte. You also can be fooled by a bug in formatting an extreme floating point value (+/- infinity, NaN, denormal). 3) Use two canary bytes: buffer[-1 + length] = '\xA5'; /* any non-NUL byte */ buffer[-2 + length] = '\0'; len1 = snprintf(buffer, length, format, args...); if ('\0' != buffer[-2 + length]) { /* Check both canaries and len1 carefully. */ /* Details omitted here. */ } buffer[-1 + length] = '\0'; /* Force terminate. */ --