snprintf vs. strcpy (etc.) in C
Asked Answered
E

9

10

For doing string concatenation, I've been doing basic strcpy, strncpy of char* buffers. Then I learned about the snprintf and friends.

Should I stick with my strcpy, strcpy + \0 termination? Or should I just use snprintf in the future?

Exerciser answered 9/4, 2010 at 10:12 Comment(1)
strncpy is completely out of place here. strncpy should not be used for limited-length string copying. This is not what that function does and this is not what it was developed for. The appropriate counterpart for snprintf in this context would be strlcpy (albeit it is not standard).Cirrocumulus
B
9

For most purposes I doubt the difference between using strncpy and snprintf is measurable.

If there's any formatting involved I tend to stick to only snprintf rather than mixing in strncpy as well.

I find this helps code clarity, and means you can use the following idiom to keep track of where you are in the buffer (thus avoiding creating a Shlemiel the Painter algorithm):

char sBuffer[iBufferSize];
char* pCursor = sBuffer;

pCursor += snprintf(pCursor, sizeof(sBuffer) - (pCursor - sBuffer),  "some stuff\n");

for(int i = 0; i < 10; i++)
{
   pCursor += snprintf(pCursor, sizeof(sBuffer) - (pCursor - sBuffer),  " iter %d\n", i);
}

pCursor += snprintf(pCursor, sizeof(sBuffer) - (pCursor - sBuffer),  "into a string\n");
Batchelor answered 9/4, 2010 at 10:28 Comment(5)
It'd be even clearer if you used a single call to snprintf. And be aware that if the buffer isn't large enough, snprintf will return the necessary buffer size (or a negative number on failure), so incrementing pCursor like that can be dangerous.Twum
Do a s/snprint/snprintf/ in your code. Also, your example doesn't make sense. I'd stick to formatters.Negus
@Negus - whoops, thanks fixed function name. Yeah, it's a dumb example, made it a bit more useful.Batchelor
@Twum - yeah, I know, either needs to have known bounds on the string size or some error checking.Batchelor
man snprintf "If an output error is encountered, a negative value is returned."Pagel
I
11

As others did point out already: Do not use strncpy.

  • strncpy will not zero terminate in case of truncation.
  • strncpy will zero-pad the whole buffer if string is shorter than buffer. If buffer is large, this may be a performance drain.

snprintf will (on POSIX platforms) zero-terminate. On Windows, there is only _snprintf, which will not zero-terminate, so take that into account.

Note: when using snprintf, use this form:

snprintf(buffer, sizeof(buffer), "%s", string);

instead of

snprintf(buffer, sizeof(buffer), string);

The latter is insecure and - if string depends on user input - can lead to stack smashes, etc.

Inanimate answered 10/4, 2015 at 7:3 Comment(1)
This inconsistency is a very confusing part for libc.Ambrosane
B
9

For most purposes I doubt the difference between using strncpy and snprintf is measurable.

If there's any formatting involved I tend to stick to only snprintf rather than mixing in strncpy as well.

I find this helps code clarity, and means you can use the following idiom to keep track of where you are in the buffer (thus avoiding creating a Shlemiel the Painter algorithm):

char sBuffer[iBufferSize];
char* pCursor = sBuffer;

pCursor += snprintf(pCursor, sizeof(sBuffer) - (pCursor - sBuffer),  "some stuff\n");

for(int i = 0; i < 10; i++)
{
   pCursor += snprintf(pCursor, sizeof(sBuffer) - (pCursor - sBuffer),  " iter %d\n", i);
}

pCursor += snprintf(pCursor, sizeof(sBuffer) - (pCursor - sBuffer),  "into a string\n");
Batchelor answered 9/4, 2010 at 10:28 Comment(5)
It'd be even clearer if you used a single call to snprintf. And be aware that if the buffer isn't large enough, snprintf will return the necessary buffer size (or a negative number on failure), so incrementing pCursor like that can be dangerous.Twum
Do a s/snprint/snprintf/ in your code. Also, your example doesn't make sense. I'd stick to formatters.Negus
@Negus - whoops, thanks fixed function name. Yeah, it's a dumb example, made it a bit more useful.Batchelor
@Twum - yeah, I know, either needs to have known bounds on the string size or some error checking.Batchelor
man snprintf "If an output error is encountered, a negative value is returned."Pagel
C
5

snprintf is more robust if you want to format your string. If you only want to concatenate, use strncpy (don't use strcpy) since it's more efficient.

Chengteh answered 9/4, 2010 at 10:15 Comment(1)
Quote from Linux man on strncpy: "Warning: If there is no null byte among the first n bytes of src, the string placed in dest will not be null-terminated." So it's not only about efficiency.Pagel
R
5

I think there is another difference between strncpy and snprintf.

Think about this:

const int N=1000000;
char arr[N];
strncpy(arr, "abce", N);

Usually, strncpy will set the rest of the destination buffer to '\0'. This will cost lots of CPU time. While when you call snprintf,

snprintf(a, N, "%s", "abce");

it will leave the buffer unchanged.

I don't know why strncpy will do that, but in this case, I will choose snprintf instead of strncpy.

Resort answered 21/3, 2012 at 23:8 Comment(0)
D
3

sprintf has an extremely useful return value that allows for efficient appending.

Here's the idiom:

char buffer[HUGE] = {0}; 
char *end_of_string = &buffer[0];
end_of_string += sprintf( /* whatever */ );
end_of_string += sprintf( /* whatever */ );
end_of_string += sprintf( /* whatever */ );

You get the idea. This works because sprintf returns the number of characters it wrote to the buffer, so advancing your buffer by that many positions will leave you pointing to the '\0' at the end of what's been written so far. So when you hand the updated position to the next sprintf, it can start writing new characters right there.

Constrast with strcpy, whose return value is required to be useless. It hands you back the same argument you passed it. So appending with strcpy implies traversing the entire first string looking for the end of it. And then appending again with another strcpy call implies traversing the entire first string, followed by the 2nd string that now lives after it, looking for the '\0'. A third strcpy will re-traverse the strings that have already been written yet again. And so forth.

So for many small appends to a very large buffer, strcpy approches (O^n) where n is the number of appends. Which is terrible.

Plus, as others mentioned, they do different things. sprintf can be used to format numbers, pointer values, etc, into your buffer.

Dysgenics answered 9/4, 2010 at 12:19 Comment(3)
Woops, I just noticed you asked specifically about snprintf. The idiom can still work, but you need to also keep a running count of how few characters remain in the buffer. It then becomes written = snprintf(buf, remanining, ...); buf += written; remaining -= written; Dysgenics
Extra care is needed with snprintf. See my comment to therefromhere's answer.Twum
As you noticed, the question is about snprintf() not sprintf(). Watch out for errors in the return value from snprintf() though! It could be negative! See here: cplusplus.com/reference/cstdio/snprintf.Tuyere
N
2

All *printf functions check formatting and expand its corresponding argument, thus it is slower than a simple strcpy/strncpy, which only copy a given number of bytes from linear memory.

My rule of thumb is:

  • Use snprintf whenever formatting is needed.
  • Stick to strncpy/memcpy when only need to copy a block of linear memory.
  • You can use strcpy whenever you know exatcly the size of buffers you're copying. Don't use that if you don't have full control over the buffers size.
Negus answered 9/4, 2010 at 10:24 Comment(0)
A
1

strcpy, strncpy, etc. only copies strings from one memory location to another. But, with snprint, you can do more stuff like formatting the string. Copying integers into buffer, etc.

It purely depends on your requirement which one to use. If as per your logic, strcpy & strncpy is already working for you, there is no need to jump to snprintf.

Also, remember to use strncpy for better safety as suggested by others.

Anta answered 9/4, 2010 at 10:25 Comment(2)
I know what you mean, but strncpy isn't restricted to reading (or even writing) NUL-terminated strings.Twum
Yes, I know. Probably, I didn't phrase my answer correctly. I have edited it. Thanks for pointing it out.Anta
P
1

The difference between strncpy and snprintf is that strncpy basically lays on you responsibility of terminating string with '\0'. It may terminate dst with '\0' but only if src is short enough.

Typical examples are:

strncpy(dst, src, n);
// if src is longer than n dst will not contain null
// terminated string at this point
dst[n - 1] = '\0';
snprintf(dst, n, "%s", src); // dst will 100% contain null terminated string
Pagel answered 1/6, 2021 at 11:53 Comment(0)
F
0

I used to use the following code to concat strings:

///Concat 2 strings. 
char *String_cat(char *s1, char *s2) {
    size_t sz1 = strlen(s1);
    size_t sz2 = strlen(s2);
    s1 = realloc(s1, 1+sz1+sz2);
    if(s1)
         (void)snprintf(s1+sz1, 1+sz2, "%s", s2);
    return s1;
}

And for N strings:

///Concat N strings. The last must be NULL
///Use:
///  char buf[] = "";
///  buf = String_catN(buf, "Hello", " to the ", "C World!", NULL);
///  printf("buf=%s\n", buf);
///  free(buf);
char *String_catN(char *s1, ...) {
    char *s2;
    va_list ap;
    va_start(ap, s1);
    s2 = va_arg(ap, char*);
    while(s2) {
        s1 = String_cat(s1, s2);
        s2 = va_arg(va, char*);
    }
    va_end(va);
    return s1;
}

Pros:

  • Better memory management (I need malloc and free just once)
  • Work with buffer size not known, then is more secure
  • '\0' terminator automatically handled

Cons:

  • Less performant than strncpy, but I believe is negligible.
Fortis answered 27/6 at 2:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.