3

I know strncpy is a safer version of strcpy as said here.

However, when I want to copy from src to dst and dst is not a clean buffer, I get unwanted results, which can be avoided by strcpy:

char *p = "123";
char a[10] = "aaaaaa";

strncpy(a,p,strlen(p));
printf("%s\n", a);   // 123aaa

strcpy(a,p);
printf("%s\n", a);   // 123 <- desired output, as the trailing a's are garbage

In my actual case, I know strlen(src) < sizeof(dst) (at least, if that isn't the case, the program will crash a lot sooner), so I can safely strcpy.

However, if strncpy is what I should use, then must I add after dst[strlen(src)] = '\0' to avoid garbage (or maybe better yet, init the buffer beforehand?)?

CIsForCookies
  • 12,097
  • 11
  • 59
  • 124
  • 2
    You are defeating the whole advantage of `strncpy` over `strcpy` by this usage. `strncpy(a,p,strlen(p) + 1);` (the `+1` is ommited from your code, but I guess you meant it) is fully equivalent to `strcpy(a,p);` – Eugene Sh. Sep 06 '18 at 15:13
  • 9
    `I know strncpy is a safer version of strcpy` It is not. it is *different* (and nearly useless) – joop Sep 06 '18 at 15:14
  • If you copy the exact length of the source string, then [`strncpy`](https://en.cppreference.com/w/c/string/byte/strncpy) will never add the terminator. You might want to use [`memcpy`](http://en.cppreference.com/w/c/string/byte/memcpy) with `strlen(p) + 1` as the length instead. But ***only*** if you know that the length of the source absolutely is smaller than the size of the destination. – Some programmer dude Sep 06 '18 at 15:14
  • To add to what @joop said, `strlcpy` is considered safer (again, if used properly). – Eugene Sh. Sep 06 '18 at 15:16
  • Your use is only *one of the things* that could go wrong when you use `strncpy()` – joop Sep 06 '18 at 15:16
  • 2
    Short answer: don't use `strncpy`. – Jabberwocky Sep 06 '18 at 15:25
  • @Jabberwocky I would not be *that* categorical... – Eugene Sh. Sep 06 '18 at 15:26
  • @EugeneSh. yep, removed "_never_" from comment. – Jabberwocky Sep 06 '18 at 15:28

3 Answers3

11

The third argument of strncpy is meant to represent the size of the target buffer. And when it fills it up, it doesn't add a null terminating character by design.

If you have sufficient space for the terminator and you insist on strncpy, just pass strlen(p) + 1 so it will not assume it exhausted the target buffer.

Like many already noted by now. This use of strncpy defeats the purpose, and is really no better than than a simple call to strcpy. The only practical use for strncpy, is if you want to overwrite a part of the string in-place (which is the use case you stumbled upon). Though that too is questionable use...

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
7

how to use strncpy correctly?

When code needs to copy a string to a destination and tolerates the result being not null character terminated nor fully copied, use the sizeof destination for the size argument:

char a[10];
// strncpy(a,p,strlen(p));
strncpy(a, p, sizeof a);

// printf("%s\n", a);
printf("%.*s\n", (int) sizeof a, a);

When code wants to copy a string and detect insufficient memory problems via strncpy() or needs null character '\0' padding, also use the sizeof destination.

char a[10];
strncpy(a, p, sizeof a);
if (a[sizeof a - 1] != '\0') {
  // insufficient memory
  // Maybe set last last character to the null character 
  a[sizeof a - 1] == '\0';
  // or other more robust handling
  return ERROR_INSUFFICIENT_MEMORY;
}

Otherwise do not use strncpy()


There are more efficient ways to detect insufficient memory than via strncpy() when null character '\0' padding is not needed. strncpy() zero fills the rest, if any, of the un-copied buffer. The below consumes much time zero-filling just to provide a insufficiency check.

char a[1000];
strncpy(a, "abc", sizeof a);
if (a[sizeof a - 1] != '\0') {
  ....

Better alternatives employ strlen(), strlcpy(), memcpy(). @Deduplicator.

See also strncpy or strlcpy in my case.

For a standard C lib one-liner, code could use snprintf() and a line of error detection. A good compiler would be expected to analyse snprintf(a, sizeof a, "%s", p) and emit efficient code.

// Copy with no overflow.
// 'a' is always null character terminated.
int len = snprintf(a, sizeof a, "%s", p);
if (len < 0 || (unsigned) len >= sizeof a) Report_truncated_copy();
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
2

strncpy() isn't actually a string-function; instead, it deals with which zero-padded sequences of non-zero characters, which together have a known fixed length. It's for example the right tool to fill in data-structures which are thereafter sent to other programs, to the outside, or persisted, to avoid data-leaks.

Trying to use it outside its very specialized niche leads to hideous contortions, and inefficient code.

There are more appropriate ways to go about it, like strlcpy() and/or manually using strlen() and memcpy().

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • 2
    Another use is preparing input for fixed-length hashes, in which it is actually important that trailing bytes have a deterministic value so that the hash is the same independent of padding. – rici Sep 06 '18 at 17:53