strncpy doesn't do what you think
/* This is the behavior a reasonable person would have given to strncpy, but
* its not the behavior strncpy has. */
char *reasonable_strncpy(char *dst, const char *src, size_t dst_size) {
if (dst_size != 0) {
dst_size--;
for (; *src && dst_size; ++src, --dst_size) {
*dst++ = *src;
}
*dst = 0;
}
return dst;
}
This post is inspired by Lars Wirzenius' "strncpy? just say
no," which made the rounds on proggit a day
or two ago. The post points out the first of strncpy
's two unexpected
behaviors. I'm writing this to point out the second. But for completeness, I point them both out.
Behind Door #1
The first suprise:
strncpy
doesn't guarantee that the resulting string indst
is NUL terminated. If there isn't enough space, the result is merely truncated.
Consider the following concrete example:
#include <stdio.h>
#include <string.h>
int main(void) {
char buf[8] = "XXXXXXX";
strncpy(buf, "x-ray", 4);
puts(buf);
return 0;
}
prints x-rXXXX
, not x-
. If you pass strncpy
the size of your buffer as the
third parameter, it may leave you with an unterminated string.
Behind Door #2
The second unexpected behavior comes up much less often, but I've seen it cause bugs in real code:
strncpy
will always writedst_size
bytes todst
, even if the source string is smaller.
For example:
#include <stdio.h>
#include <string.h>
int main(void) {
char buf[16];
strcpy(buf+8, "back");
strncpy(buf, "front", 16);
puts(buf+8);
return 0;
}
prints nothing, rather than back
.
The Solution
If truncation is acceptable:
strncpy(dst, src, sizeof dst - 1);
dst[sizeof dst - 1] = 0;
/* snprintf was added in C99, but it or an equivalent function is present in
* most C89 implementations. */
snprintf(dst, sizeof dst, "%s", src);
If truncation isn't acceptable, you should consider whether or not C is the best language for the task at hand. If it really is, then you should use one of the many C string libraries. bstring is popular, portable and liberally licensed, although I've never used it myself.