strncpy

John S. Price john at stat.tamu.edu
Wed Jan 24 12:31:33 AEST 1990


In article <11881 at csli.Stanford.EDU> poser at csli.stanford.edu (Bill Poser) writes:
>
>The manual page does indeed say that strncpy(3) copies exactly
>N characters, but as William Davidsen has pointed out, the manual,
>at least on some systems, is wrong. Here is a little test program.
>
>#include <stdio.h>
>main()
>{
>   char src[20];
>   char tgt[20];
>
>   sprintf(src,"%s","abcdefg");
>   printf("src = %s\n",src);
>   src[3] = '\0';
>   printf("src = %s\n",src);
>   strncpy(tgt,src,5);
>   printf("tgt = %s\n",tgt);
>
>   exit(0);
>}
>
>It puts the string "abcdefg" (7 characters plus a null) into src
>and prints it out. Then it assigns a null to the 4th position, overwriting
>the "d" and prints it out. Then it strncpy's it to tgt, requesting a 5
>character copy, and prints the result. I just compiled and ran this program
>on: (a) a SUN 4 running SUN-OS; (b) an HP 9000/350 running HP-UX, and
>(c) an HP 9000/320 running 4.3 Tahoe BSD. On all three the result was that
>the null byte terminated the copy. Here is the script from the HP 350:
>
>Script started on Tue Jan 23 14:58:27 1990
>crystals-[1]/user2/poser
>: foo
>src = abcdefg
>src = abc
>tgt = abc
>crystals-[2]/user2/poser
>: 
>script done on Tue Jan 23 14:58:33 1990
>
>If strncpy copied exactly N characters, the third output line should
>read: tgt = abcde
>On all three systems the manual says that strncpy copies exactly N
>characters, and on all three it is wrong.

Every manual I read says this:
"... If the string pointed to by the source has fewer than count
characters, nulls are added to the end of dest until count characters 
have been copied." -- C - The Complete Reference.

"... strncpy(s,t,n)    copy exactly n characters; null pad if necessary"
 -- Unix Programming Environment

The key to this is the way C, or for that matter, ASCIIZ, string
convention works.  A series of characters followed by a NULL is
considered a string.  So, if you have a string

foo[] = "This is a test of the Emergency Broadcast System.";

and say

foo[14] = NULL;

You are CHANGING THE STRING LENGTH.  It is now 14 characters long,
instead of 49.  

back to the example in question...

The only was strncpy can know if the source has fewer than count (in the
program example 5) characters is if it's null terminated or not.  So, when
you say 

src[3]=0;

you, by definition of the ASCIIZ convention, have shortened the string
to 3 characters, namely "abc".  When strncpy(tgt,src,5) gets a pointer to

"abc\000efg"

for the source, all it considers to be the STRING is

"abc"

and that is all it copies to tgt.  It pads tgt with NULLs up to N, 
the number of characters specified to copy.

You have to remember that all of these are STRING FUNCTIONS, not
block memory move functions.  If you wanted to copy the 5 bytes
from src to tgt, do

memcpy(tgt,src,5);

But, printf would still print out "abc" for tgt, because it
is NULL terminated.  Only difference is that tgt[4]=='e' now, 
instead of NULL.

strncpy() is acting exactly to the manuals specs.  When it says
is will copy EXACTLY 5 characters from the STRING src to the
destination STRING tgt, it copys 5 characters from the string.
Since strings are NULL terminated, strncpy has to assume
that anything after the NULL is garbage, and not part
of the string.  It assumes that tgt will have N bytes allocated
to it, but doesn't assume that src has at least N bytes.

If src is larger than N, then it copys N characters to tgt.
It doesn't NULL terminate tgt because it can't assume that
tgt is N+1 bytes long.

Just to clear things up...

--------------------------------------------------------------------------
John Price                   |   It infuriates me to be wrong
john at stat.tamu.edu           |   when I know I'm right....
--------------------------------------------------------------------------



More information about the Comp.lang.c mailing list