Richard said:
That's one of the problems with strncpy. There are plenty more. For a
start, what if you incorrectly specify the third parameter? (It happens,
believe me.)
The first problem is a strawman. Since the third argument is well known,
and due to the way standard specifies strncpy must behave, you only have to
check and see if dst[n-1] is '\0' or not, to tackle this situation.
The second is a bogeyman that you can use to scare anyone off any library
function. Idiots can wreck havoc with anything. By your logic, we should
probably never use snprintf, fgets, fread, fwrite, memcpy, memmove,
strncmp, strncat, malloc, realloc and such like. I am not saying that this
mistake is not made, but only that a fear of such mistakes will result in
total paralysis that prevents you from writing any meaningful program. C
does not have bounds checking. You should be aware of the risks you take
when you program in this language.
strncpy is pretty useful when you know that most of the time your input is
going to be less than a certain amount of characters. It can be used
safely by those who pay due care to what it is supposed to do.
Finding out how big the source is, is easy. strlen() does it. Presumably
you know how big your target is. So call strlen on your source, and check
that the receiving buffer is big enough. If it isn't, well, Houston, we
have a problem. We might have been clever enough to make the target buffer
dynamic, in which case we can resize. If not, well, we're stuck.
If I know that most of the time my input is between 18-20 characters, why
waste time with strlen, malloc and strcpy? I would go for an array of 21
characters, try to strncpy 21 characters into it, and check if array[20] is
'\0' or not after the strncpy to see if I've hit the rare case.
Terms like "data you need" start to get rather subjective; if you saw
a piece of code such as:
strncpy(dest, source, 50);
it would be hard to argue that the coder "needs" stuff past the first
50 characters,
It would be hard to argue that the coder thought the problem through. If
he only wants the first 50 characters, why can the source buffer hold more
than 50? (If it can't, strcpy will work fine.) And why not just nail it:
source[49] = '\0';
strcpy(dest, source);
I qualify my input parameters with const as far as possible. Modifying the
source unnecessarily is not only not an option, but is also bad style in my
books. Also, if this is a solution, so is:
dst[49] = 0;
strncpy(dst, src, 49);
In the majority of cases (i.e. strlen(source) < 50), this code will be
quicker, since it will have to write fewer bytes. And it's never slower.
Only if you are silly enough not to check that your receiving buffer is
big enough. And so can strncpy lead to a buffer overrun, for the same
reason.
I can twist that argument around to tackle your bogeyman "third argument
incorrect" argument against strncpy: "Only if you're silly enough to pass
the wrong size for the receiving buffer", as you yourself go on to point
out. So, there we go: that argument is a bogeyman by your own admission
Barring that bogeyman argument, if I used strncpy, I *don't* have to check.
All I have to check is that the src was no longer than I expected, which
can be done in a very straight-forward and simple manner. In fact, you can
(and I do), wrap these operations into a function and use it safely. IMHO,
creating a buffer overrun with strncpy is less likely than with strcpy.
YMMSTV. Of course, if you always wanted all of the source regardless of
size, well, that's what strcpy is for
That's enough. But look at just a few of the things that could go wrong:
strncpy(target, source, sizeof target); /* possibly no null terminator */ strawman.
strncpy(target, source, sizeof source); /* tyop in third parameter! */ bogeyman.
strncpy(target, source, strlen(target)); /* less data than you hoped? */
bogeyman. malloc(strlen(target))... One sees the mushroom cloud.
strncpy(target, source, sizeof target + 1); /* instead of - 1 */
Nope. sizeof target is perfectly fine. It is because of misunderstanding
strncpy that you're playing around with the -1, +1 stuff. Also, if you
ever see that code and don't realize you're making a mistake, you probably
shouldn't be programming in C. (That's a metaphorical you, not "you" RJH).
Three of these four can lead to a buffer overrun.
strcpy(dst, src);
gives me no information about possible errors. Atleast, strncpy tells me
how much will be written.
The comparison is invalid. The gets() function /cannot/ be used safely,
whereas strcpy can be. It's a sharp tool, and you can cut yourself on it
if you're not careful, but it is a powerful and legitimate tool in the
hands of a competent practitioner.
Yes. That is not a fair comparison. But strncpy is a perfectly safe and
useful function, that you should try to use if appropriate, just as you
should use strcpy when appropriate.
The fgets/strncpy comparison is probably fair. I rarely use fgets() in
real code, because it's too awkward.
This comparison is also not fair. fgets is idiotic. It may or may not
leave a newline in your buffer and may or may not consume a complete line
of input. All the while, it gives you the false illusion that you can use
it to read one "line" from the input stream. There is no easy way to check
how much exactly was read by fgets. If I had a choice, I would make fgets
return the number of characters read instead of uselessly returning the dst
pointer. scanf is complicated and its usage is error-prone (by me
atleast). However, I usually try to take the time to get a working scanf
solution when it is feasible and shun fgets. strncpy on the other hand
suffers none of those drawbacks. You know exactly, in one comparison,
whether you had enough space for all of the source, and whether your dst is
a valid string or not.
My point is that all library functions have pros and cons. Some like gets
are hopelessly broken. But being dogmatic about rejecting one and favoring
the other without thinking the issues through is ridiculous. So, yes,
there are good, valid situations where strncpy is an excellent fit for the
problem. Don't blindly reject it in favor of strcpy.
-nrk.