Reputation: 922
I just realized that I am in the habit of using snprintf
for taking slices of strings, which now seems unconventional to me for several reasons (chiefly, the need to #include <stdio.h>
). But after looking at strncpy
more closely, I'm struggling to see why anyone would prefer it over snprintf
.
To be clear, I'm comparing strncpy(dest, src, count)
with snprintf(dest, count + 1, "%s", src)
. snprintf(dest, count + 1, src)
obviously causes problems whenever src
contains '%'
.
What stood out to me most was how much more snprintf
acts like an str
- function than strncpy
, which by comparison feels more like a mem
- function to me.
strncpy
reaches the end of src
before it copies count
characters, it will write count - strlen(src)
null characters in addition to the terminating null character copied from src
.
mem
- functions would even notice the additional characters, but I can't imagine the difference would ever be particularly useful. Also, if you're actually dealing with byte arrays, why are you using string functions?char foo[6] = "hi";
doesn't fill the remaining 3 bytes with null characters! Furthermore, in my experience, arrays are typically pre-initialized in this manner using... you guessed it, memset
. A mem
- function.strncpy
copies count
characters before it reaches the end of src
, the resulting character array won't be null-terminated. That's right, the string copying function sometimes doesn't produce a valid string. What?strncpy
's count
argument doesn't include the terminating null character. This makes memory allocation a bit awkward since the character array must be allocated with at least count + 1
bytes in order to store count
characters as a proper null-terminated string. Even worse, the last character in the array still needs to be set to the null character, which you can only do with strncpy
if you know that strlen(src) == count + 1
.Compare all of this to snprintf
:
snprintf
copies at most count - 1
characters and a null character, meaning you need an array of count
elements to store it all. Sweet!snprintf
reaches the end of src
before it copies count - 1
characters, it just adds a terminating null character to dest
and halts - no questions asked.snprintf
copies count - 1
characters before it reaches the end of src
, it still adds a terminating null character to the end of dest
.Why on earth is strncpy
this way?
Upvotes: 0
Views: 994
Reputation: 48010
What is the advantage of using
strncpy
oversnprintf
?
Virtually none. The only advantage of strncpy
(over anything) is if you want to create what the C FAQ list calls a "now-obsolete data structure, the fixed-length, not-necessarily-\0
-terminated 'string.'"
If your goal is to copy the string src
to the destination dst
, as a proper (but possibly truncated) string, and without overflowing dst
's size dstsize
, then
snprintf(dst, dstsize, "%s", src);
is actually a pretty good way to do it. (Not the only way, and not a bad way, but perhaps overkill.)
On the other hand, strncpy(dst, src, dstsize)
, is no solution to this problem at all.
Other possible solutions to this problem are
*dst = '\0'; strncat(dst, src, dstsize);
strlcpy(dst, src, dstsize);
strcpy_s(dst, dstsize, src);
Why on earth is
strncpy
this way?
In the early days of C and Unix, it was somewhat common to use short, fixed-length pseudo-strings which were not necessarily null-terminated, but which were padded out to their "full length" with extra \0
's if necessary. This meant that you could efficiently compare them using the equivalent of memcmp
; you didn't have to check for \0
at all. See also the footnote to the aforementioned C FAQ list entry.
strncpy
is pretty much obsolete today; many programmers recommend avoiding it entirely.
Upvotes: 2
Reputation: 44329
What is the advantage of using
strncpy
oversnprintf
?
Well, it doesn't really make sense to compare two functions that are really intended to do different things.
strncpy
is designed to write exactly n
characters to dst
. Some or all (but at least 1) of these n
characters may be copied from the string src
and the remaining characters (if any) will be null characters. Unfortunately many C programmers think that strncpy
is a "safe" version of strcpy
but it's not and it's not what was intended. The name strncpy
is probably what is misleading.
snprintf
is designed for a completely different task, i.e. formatted printing to a string.
Special use cases of sprintf
like sprintf(dst, n, "%s", src)
may seem equivalent to strncpy(dst, src, n)
because the result dst
when viewed as a "traditional C style" string looks the same. But it's not truely identical calls. They do different things to generate the result dst
and dst
may be different when looking at all array elements (see example in the end of this answer).
Why on earth is strncpy this way?
strncpy
is handy when working with strings stored in fixed size arrays where no termination character is used and null padding is used for strings shorter than the array size.
Example of snprintf
appearing to do the same as strncpy
while it's not
int main ()
{
char src[] = "Hi";
char dst[6];
strcpy(dst, "Hello");
puts(dst);
strncpy(dst, src, 6);
puts(dst);
puts("------------------");
strcpy(dst, "Hello");
puts(dst);
snprintf(dst, 6, "%s", src);
puts(dst);
return 0;
}
Output
Hello
Hi
------------------
Hello
Hi
From the output it seems that the strncpy
call and the snprintf
call did the same, i.e. changed dst
from "Hello" to "Hi".
However, a closer look at dst
- like
void pp(const char* p)
{
for (int i=0; i < 6; ++i) printf("%02X ", p[i]);
puts("");
}
int main ()
{
char src[] = "Hi";
char dst[6];
strcpy(dst, "Hello");
puts(dst);
strncpy(dst, src, 6);
puts(dst);
pp(dst);
puts("------------------");
strcpy(dst, "Hello");
puts(dst);
snprintf(dst, 6, "%s", src);
puts(dst);
pp(dst);
return 0;
}
Output
Hello
Hi
48 69 00 00 00 00
------------------
Hello
Hi
48 69 00 6C 6F 00
shows that the two calls actually resulted in different dst
values.
Upvotes: 3