C/C++ Arena

Step 6 of 8

Safe formatting with snprintf

sprintf(buf, format, ...) works like printf but writes into a char array. The problem: it has no idea how big buf is, so a long name or a big number can write past the end. That exact bug has caused countless security holes.

snprintf takes the buffer size as its second argument and never writes more than that, always ending with a '\0' (as long as the size isn't 0):

#include <stdio.h>

int main(void) {
    volatile int score = 12345;
    char small[8];
    int needed = snprintf(small, sizeof small, "score=%d", score);
    printf("[%s] needed %d\n", small, needed);
    char big[32];
    needed = snprintf(big, sizeof big, "score=%d", score);
    printf("[%s] needed %d\n", big, needed);
    return 0;
}
[score=1] needed 11
[score=12345] needed 11

(volatile just stops the compiler from working out the value in advance and warning about the truncation we're demonstrating on purpose.)

Detecting truncation

The return value is the length the full output would have had (not counting the terminator), whether or not it fit. So:

A careful function reports truncation to its caller instead of silently producing a clipped string. Since needed is an int and size a size_t, compare them as (size_t)needed >= size after checking needed isn't negative.

Use snprintf for all formatting into buffers; there's no good reason to use sprintf in new code.

Your task

Write int format_kd(char *buf, size_t size, const char *name, int kills, int deaths) that writes NAME K/D (like ropz 25/12). Return 0 if it fit, -1 if it was truncated.

Challenge 2

Write int format_time(char *buf, size_t size, int seconds) that writes the time as M:SS, like 2:05 for 125 seconds. The seconds always have two digits (%02d). Return 0 if it fit, -1 if it was truncated.

Challenge 3

Write int join_names(char *buf, size_t size, const char **names, int n) that writes the names separated by , (like ropz, rain, broky). Build it with several snprintf calls, each writing at the current end of the text. Return 0 if everything fit, or -1 if it had to be cut short; the buffer must always end up terminated.

Previous: Appending Next: Binary files