27

I am using snprintf like this to avoid a buffer overrun:

char err_msg[32] = {0};
snprintf(err_msg, sizeof(err_msg) - 1, "[ ST_ENGINE_FAILED ]");

I added the -1 to reserve space for the null terminator in case the string is more than 32 bytes long.

Am I correct in my thinking?

Platform:

  • GCC 4.4.1
  • C99
3
  • 2
    Side note: GCC doesn't support C99: gcc.gnu.org/c99status.html Commented Nov 21, 2009 at 13:53
  • 2
    Are you aware, though, of a modern environment where the gcc and standard library combo doesn't include snprintf? Commented Nov 21, 2009 at 18:15
  • 3
    When I was using MinGW one or two years ago, it actually called Microsoft's _snprintf(), which doesn't behave like the standard snprintf() (I think it doesn't always nul-terminate the string). Commented Nov 21, 2009 at 19:25

6 Answers 6

42

As others have said, you do not need the -1 in this case. If the array is fixed size, I would use strncpy instead. It was made for copying strings - sprintf was made for doing difficult formatting. However, if the size of the array is unknown or you are trying to determine how much storage is necessary for a formatted string. This is what I really like about the Standard specified version of snprintf:

char* get_error_message(char const *msg) {
    int e = errno;
    size_t needed = snprintf(NULL, 0, "%s: %s (%d)", msg, strerror(e), e);
    char  *buffer = malloc(needed+1);
    if (buffer) sprintf(buffer, "%s: %s (%d)", msg, strerror(e), e);
    return buffer;
}

Combine this feature with va_copy and you can create very safe formatted string operations.  

Sign up to request clarification or add additional context in comments.

10 Comments

Don't use strncpy() if there string might be too big to fit into the target; strncpy() does NOT null-terminate what it copies if it is too long. Further, it always copies N characters - even if the source is 1 byte and the target is 1 megabyte.
This code has a bug: "Upon successful completion, the snprintf() function shall return the number of bytes that would be written to s had n been sufficiently large excluding the terminating null byte." Thus you have to allocate an additional byte for the terminating null.
@JeremySalwen - thanks for catching that... that's what I get for writing Java =)
You could simply use sprintf(buffer, …, since you have reserved buffer of the exact right size. That would avoid this bug.
@PascalCuoq very good point.... not sure how I missed this one or how it hasn't been noticed for four and a half years
|
15

You don't need the -1, as the reference states:

The functions snprintf() and vsnprintf() do not write more than size bytes (including the trailing '\0').

Note the "including the trailing '\0'" part

Comments

10

No need for -1. C99 snprintf always zero-terminates. Size argument specifies the size of output buffer including zero terminator. The code, thus, becomes

char err_msg[32];
int ret = snprintf(err_msg, sizeof err_msg, "[ ST_ENGINE_FAILED ]");

ret contains actual number of characters printed (excluding zero terminator).

However, do not confuse with Microsoft's _snprintf (pre-C99), which does not null-terminate, and, for that matter, has completely different behaviour (e.g. returning -1 instead of would-be printed length in case if buffer is not big enough). If using _snprintf, you should be using the same code as in your question.

Comments

3

According to snprintf(3):

The functions snprintf() and vsnprintf() do not write more than size bytes (including the trailing '\0').

Comments

0

For the example given, you should be doing this instead:

char err_msg[32];
strncpy(err_msg, "[ ST_ENGINE_FAILED ]", sizeof(err_msg));
err_msg[sizeof(err_msg) - 1] = '\0';

or even better:

char err_msg[32] = "[ ST_ENGINE_FAILED ]";

3 Comments

As noted in a comment to another answer: Don't use strncpy() if there string might be too big to fit into the target; strncpy() does NOT null-terminate what it copies if it is too long. Further, it always copies N characters - even if the source is 1 byte and the target is 1 megabyte.
@Jonathan Leffler, your description of how many bytes strncpy() is incorrect. "At most n bytes of src are copied." I've adjusted the example to fix the null-termination.
@anacrolix: your example does not guarantee NULL termination. It does guarantee that strncpy() will never overwrite the last character in the buffer. You have to explicitly do err_msg[sizeof(err_msg)-1] = '\0'; to get termination.
-1

sizeof will return the number of bytes the datatype will use in memory, not the length of the string. E.g. sizeof(int) returns '4' bytes on a 32-bit system (well, depending on the implementation I guess). Since you use a constant in your array, you can happily pass that to the printf.

2 Comments

He's applying sizeof to the destination array, which is completely correct.
32 is correct. In that case he does not want the size of the string (which is given by strlen) he wants the err_msg buffer capacity (to guarantee it will not write past its end).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.