[comp.lang.c] strncpy question

pts@watt.acc.Virginia.EDU (Paul T. Shannon) (05/10/89)

I have a question on the strncpy library function. (The same question applies to
strcpy as well.) This program fragment seems to be the recommended usage:

       char *original, *copy;   /* assume that original points to a string */
       int stringLength;

       stringLength = strlen (original);
       copy = malloc (stringLength + 1);  /* allow room for terminating null */
       copy = strncpy (copy, original, stringLength); 

Why is this function written so that the pointer to the destination string is 
both
       1. passed as  an argument to the function, and
       2. returned by the function?

Paul Shannon  

scs@sloth.pika.mit.edu (Steve Summit) (05/11/89)

In article <630@babbage.acc.virginia.edu> pts@watt.acc.Virginia.EDU (Paul T. Shannon) writes:
>Why is [strncpy] written so that the pointer to the destination string is 
>both
>       1. passed as  an argument to the function, and
>       2. returned by the function?

I think the answer is "because nobody could think of anything
else for it to return."  (It is occasionally suggested, both for
str[n]cpy and str[n]cat, that returning a pointer to the end of
the copied characters would have been more useful.)  The one use
I've seen for str[n]cpy's return value is the following idiom,
which is close to what you were trying to do:

	copy = strcpy(alloc(strlen(original) + 1), original);

The purported advantage here is that you only have one assignment
to "copy," which is more "concise."  The disadvantage is that
someone reading the code might be puzzled.  It's probably not
worth arguing about; I tend to split my usage between the idiom
and the two-line form

	copy = alloc(strlen(original) + 1);
	(void)strcpy(copy, original);

depending on what mood I'm in.  (Note that the one-line form
absolutely depends on a "safe" wrapper around malloc, that checks
for a NULL return and prints an error message and exits or
something rather than returning NULL.  I usually call my wrapper
function "alloc," as in the examples above.)

Returning to your code, you don't want to use strncpy after all:
>
>       char *original, *copy;   /* assume that original points to a string */
>       int stringLength;
>
>       stringLength = strlen (original);
>       copy = malloc (stringLength + 1);  /* allow room for terminating null */
>       copy = strncpy (copy, original, stringLength); 

Your copied string won't necessarily be nul-terminated, and in
fact strncpy does not guarantee nul termination in any case.  The
third argument to strncpy tells it how many characters it may
write to the destination string, including the nul termination.
If strncpy runs out of room for the nul, it won't append it.  On
those (rare, because of the lack of a guarantee) occasions when I
use strncpy, it's usually something like

	(void)strncpy(dest, src, len);
	dest[len] = '\0';	/* ensure nul termination */

(presumably, if I'm using strncpy in the first place, I can
handle truncation, so losing one more character won't hurt).

Another little-known fact about strncpy is that it pads the
destination string with nuls (\0's) out to the requested length,
even if that means appending more than one nul.  (That is,

	char buf[14] = "xxxxxxxxxxxxxx";
	strncpy(buf, "hello.c", 14);

leaves "hello.c\0\0\0\0\0\0\0" in buf.)  The only reason I know
of for this behavior is that the old V7 filesystem required it*;
if you patched a directory entry by hand, but left garbage within
a 14-character filename slot but after what you thought was a
trailing nul, the filename couldn't be found.  (The kernel
evidently did something more like memcmp than strncmp when
searching for filenames.  I'm drifting into unix-wizards
territory here; don't assume that this scenario had any affect on
normal user programs.  Henry will correct me if this didn't apply
to "vanilla" V7; it did come up in V7 derivatives I've used.)

I wouldn't depend on the nul-padding behavior, but if you're ever
writing a strncpy, you'd better provide it, because there are
undoubtedly programs out there that do depend on it.

                                            Steve Summit
                                            scs@adam.pika.mit.edu

* This is, I'll admit, a far-fetched argument; the kernel isn't
  necessarily linked against the same C library routines as are
  user programs.

rkl@cbnewsh.ATT.COM (kevin.laux) (05/11/89)

In article <630@babbage.acc.virginia.edu>, pts@watt.acc.Virginia.EDU (Paul T. Shannon) writes:
} I have a question on the strncpy library function. (The same question applies to
} strcpy as well.) This program fragment seems to be the recommended usage:
} 
	[deleted fragment]
}
} Why is this function written so that the pointer to the destination string is 
} both
}        1. passed as  an argument to the function, and
}        2. returned by the function?
} 

	The return of the destination pointer allows for the function to be
passed as an argument to other functions, as in:

	printf ("<format...> %s\n", <args>, strncpy (dest, src, count));

--rkl