1

This problem is blowing my mind...Can anyone please sort out the problem because i have already wasted hours on this.. ;(

#include <stdio.h>
#include <string.h>
int main(){

  char string[] = "Iam pretty much big string.";
  char temp1[50];
  char temp2[10];

  // strcpy() and strncpy()
   strcpy(temp1, string);
   printf("%s\n", temp1);

  strncpy(temp2, temp1, 10);
  printf("%s\n", temp2);
  return 0;
}

Result

Iam pretty much big string.
Iam prettyIam pretty much big string.

Expected Result:

Iam pretty much big string.
Iam pretty
Zafeer
  • 79
  • 7
  • 6
    `strncpy()`, despite its name, was not designed to deal with *strings*: it does not manage the terminating '`\0`' in the usual way --- specifically it may leave the destination array with no zero byte (or over write with zeros more data than needed in the context of *strings*) – pmg Jan 15 '20 at 14:51
  • 1
    @pmg, it does deal with strings, in the sense that it stops copying and starts zero-padding if it reaches the end of the input *string*. It's the output side of the function that deals with non-string records. – Toby Speight Jan 15 '20 at 15:27

5 Answers5

4

The address of temp2 is just before the address of temp1 and because you do not copy the final 0, the printf will continue printing after the end of temp2.

As time as you do not insert the 0, the result of printf is undefined.

alinsoar
  • 15,386
  • 4
  • 57
  • 74
  • 1
    nitpick #2: it's less ambiguous as `'\0'`, not `0` ;-) i also say `NUL` (with one `L` sometimes, but that will probably annoy someone – underscore_d Jan 15 '20 at 14:56
  • @underscore_d NUL is what ASCII calls the 0 byte, so if it annoys someone, blame the people who came up with it. – Shawn Jan 15 '20 at 14:59
  • Yes, I know the difference between them, I should have written NUL. But I corrected and inserted the number zero. – alinsoar Jan 15 '20 at 15:01
  • Yes, but my point is that's not the literal ASCII character `'0'`, but the escaped control character `'\0'` – underscore_d Jan 15 '20 at 15:08
  • @underscore_d ok. – alinsoar Jan 15 '20 at 15:12
  • I realise my comment to David's answer was just a rehash of your answer. I got so carried away in the fight about termination characters I failed to read the answer itself! – Edd Inglis Jan 15 '20 at 15:38
4

You invoke Undefined Behavior attempting to print temp2 as temp2 is not nul-terminated. From man 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." (emphasis in original)

See also C11 Standard - 7.24.2.4 The strncpy function (specifically footnote: 308)

So temp2 is not nul-terminated.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • _Undefined Behaviour_ could mean literally anything happens, but in real life it's often pretty easy to see what will happen / has happened. In this case, your `temp1` array is likely immediately after `temp2` on the stack, so - in the absence of the terminating `\0` - `printf` carries on reading characters and starts printing `temp1` as well, until it reaches _its_ termination. – Edd Inglis Jan 15 '20 at 15:01
  • Agreed, but if you talk about a specific stack order -- someone is bound to point out that it doesn't hold for all cases `:)` (A Hobson's Choice) Easiest solution here is just `temp2[9] = 0;` after the copy. – David C. Rankin Jan 15 '20 at 15:02
  • @DavidCRankin, you are technically correct; the best kind of correct! – Edd Inglis Jan 15 '20 at 15:04
  • i added a null character at the end of temp2 and it worked. – Zafeer Jan 15 '20 at 16:02
4

Citation of the appropriate [strncpy] tag on Stack Overflow https://stackoverflow.com/tags/strncpy/info, which may help you to understand what happens exactly:


This function is not recommended to use for any purpose, neither in C nor C++. It was never intended to be a "safe version of strcpy" but is often misused for such purposes. It is in fact considered to be much more dangerous than strcpy, since the null termination mechanism of strncpy is not intuitive and therefore often misunderstood. This is because of the following behavior specified by ISO 9899:2011 7.24.2.4:

char *strncpy(char * restrict s1, 
     const char * restrict s2, 
     size_t n);

/--/

3 If the array pointed to by s2 is a string that is shorter than n characters, null characters are appended to the copy in the array pointed to by s1, until n characters in all have been written.

A very common mistake is to pass an s2 which is exactly as many characters as the n parameter, in which case s1 will not get null terminated. That is: strncpy(dst, src, strlen(src));

/* MCVE of incorrect use of strncpy */
#include <string.h>
#include <stdio.h>

int main (void)
{
  const char* STR = "hello";
  char buf[] = "halt and catch fire";
  strncpy(buf, STR, strlen(STR));
  puts(buf); // prints "helloand catch fire"
  return 0;
}

Recommended practice in C is to check the buffer size in advance and then use strcpy(), alternatively memcpy(). Recommended practice in C++ is to use std::string instead.

4

The strncpy function is respecting the 10 byte limit you're giving it.

It copies the first 10 bytes from string to temp2. None of those 10 bytes is a null byte, and the size of temp2 is 10, so there are no null bytes in temp2. When you then pass temp2 to printf, it reads past the end of the array invoking undefined behavior.

You would need to set the size given to strncpy to the array size - 1, then manually add the null byte to the end.

strncpy(temp2, temp1, sizeof(temp2)-1);
temp2[sizeof(temp2)-1] = 0;
dbush
  • 205,898
  • 23
  • 218
  • 273
  • Thanks buddy!! I was not aware of adding null byte at the end manually...i thought this will be added automatically..btw i added '\0' instead of only 0 because it was randring 0 as an ascii code. – Zafeer Jan 15 '20 at 15:49
  • 1
    @Zafeer If the source string was less than 10 bytes long it *would* add the null terminator. However it didn't because it was 10 or more bytes long. – dbush Jan 15 '20 at 16:00
  • i understand now..that was kinda unexpected behavior for me but with the help of you guys this concept is crystal clear for me :) – Zafeer Jan 15 '20 at 16:06
3

From the manpage for 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.

Either your input is shorter than the supplied length, you add the terminating null byte yourself, or it won't be there. printf() expects the string to be properly null terminated, and thus overruns your allocated buffer.

This only goes to show that the n variants of many standard functions are by no means safe. You must read their respective man pages, and specifically look for what they do when the supplied length does not suffice.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106