Re: is strncpy useful at all?

From: Anthony Borla (ajborla_at_bigpond.com)
Date: 12/16/04


Date: Wed, 15 Dec 2004 23:20:45 GMT


"Arthur J. O'Dwyer" <ajo@nospam.andrew.cmu.edu> wrote in message
news:Pine.LNX.4.60-041.0412151140020.3459@unix44.andrew.cmu.edu...
>
> On Wed, 15 Dec 2004, Anthony Borla wrote:
> > "zentara" <zentara@highstream.net> wrote...
> >> I was looking at an example of strncpy.
> >> It was explained that it is better than strcpy because
> >> it specifies the maximum number of chars to copy.
> >> But this example just overflows anyway.
> [...]
> > You already got the explanations, so how about an example of
> > how you might implement these [well, similar] functions ?
>
> "Well, similar" indeed! Why didn't you take the extra five
> seconds and write the /correct/ function definitions?
>

Whilst I admit the return values in both functions are incorrect, and a
prototype name was mispelled, I believe the rest of the code is correct
given that:

* I was writing C, not C++, so no 'const' usage present

* Functions were not intended to be Standard-conforming,
   merely, as described, 'similar' to the library functions

> > Besides, it's an excuse to be
> > wicked and play with pointers and 'goto' ;) !
>
> Only if you're looking for an excuse. They're trivial to
> write without 'goto'.
>

Too true. Whilst the errors were not intended, the 'goto' usage *was*
deliberate:

    '...an excuse to be wicked ...' ;) !

even though, as you point out, unnecessary.

> > /* ====================================== */
> >
> > #include <stddef.h>
> >
> > char* strncpy(char* dest, char* src);
>
> BZZT!
>
> > char* strncpy(char* dest, char* src, size_t maxlen);
> >
> > int main(void)
> > {
> > char src[] = "ABCD";
> > char dest[10] = {'\0'};
> >
> > /* Some tests you may want to try ... */
> > strncpy(dest, src, 0);
> > strncpy(dest, src, 1);
> > strncpy(dest, src, 3);
> > strncpy(dest, src, 4);
> > strncpy(dest, src, 5);
> > strncpy(dest, src, 12);
>
> The last "test" invokes undefined behavior.
>
> >
> > /* Of course, these will blow up ... */
> > strncpy(NULL, src, 4);
> > strncpy(dest, NULL, 12);
>
> Correct; both invoke UB.
>
> > /* And this will blow up if 'src' isn't NUL terminated ... */
> > strncpy(dest, src, -1);
>
> Even if it is null-terminated, this one invokes UB as well.
>
> > return 0;
> > }
> >
> > char* strcpy(char* dest, char* src)
> > {
> > while (*src != '\0')
> > *dest++ = *src++;
> >
> > *dest = '\0'; return dest;
> > }
>
> This is /almost/ correct. The return value ought to be the
> old value of 'dest' (i.e., the first parameter to the function).
> You return a pointer to the terminating null byte of 'dest'.
> A correct implementation would be [UNTESTED CODE]
>
> char *strcpy(char *dst, const char *src)
> {
> char *olddst = dst;
> while ((*d++ = *s++) != '\0')
> continue;
> return olddst;
> }
>
> By the way, didn't anyone ever tell you that one-space
> tabs are unreadable? Try four spaces, or at least some
> value between 3 and 8.
>

No, but they have now - thank you, Arthur :) ! Seriously, I actually do use
2 space indentation but it may 'get lost' in the copy'n'pasting from source
code [fixed-width fonts] to e-mail client [proportional fonts].

> > char* strncpy(char* dest, char* src, size_t maxlen)
> > {
> > size_t len = 0;
> >
> > while (*src != '\0')
> > {
> > if (++len > maxlen)
> > goto STRNCPY_EXIT;
> >
> > *dest++ = *src++;
> > }
> >
> > STRNCPY_EXIT:
> > *dest = '\0'; return dest;
> > }
>
> This one is just utterly wrong, and poorly implemented to
> boot. (Why are you using 'goto' when 'break' would suffice?)
> This function tries to append a null byte to 'dest' no matter what,
> and never tries to null-pad the destination string up to 'maxlen'.
> It bears no resemblance to the standard 'strncpy' function, a correct
> implementation of which might be [UNTESTED CODE]
>
> char *strncpy(char *dst, const char *src, size_t n)
> {
> char *olddst = dst;
> while (n > 0 && (*d++ = *s++) != '\0')
> continue;
> while (n > 0)
> *d++ = '\0';
> return olddst;
> }
>

Not sure about the use of 'd' and 's', and might not 'n' need decrementing ?
Glad I'm not the only one who makes mistakes ;) !

>
> As Mike pointed out, C++ programmers should
> generally not be using either of these functions for
> string handling.
>

Indeed, but I'm pretty sure I was writing C code ;) !

Cheers,

Anthony Borla

P.S.

A slightly refined, more complete version [note 'runTest' is deliberately
not self-checking]:

/* -------------------------------------------*/

#include <stddef.h>
#include <string.h>
#include <stdio.h>

typedef char* (*FP_STRNCPY)(char* dest, char* src, size_t maxlen);

char* tstrcpy(char* dest, char* src);
char* tstrncpy(char* dest, char* src, size_t maxlen);
void runTest(FP_STRNCPY fp, char* dest, char* src, size_t maxlen,
             size_t destlen);

#define DEST_SIZE 10

int main(void)
{
  char src[] = "ABCD"; char dest[DEST_SIZE + 1];

  /* Some tests you may want to try ... */
  runTest(tstrncpy, dest, src, 0, DEST_SIZE);
  runTest(tstrncpy, dest, src, 1, DEST_SIZE);
  runTest(tstrncpy, dest, src, sizeof src - 1, DEST_SIZE);
  runTest(tstrncpy, dest, src, sizeof src, DEST_SIZE);
  runTest(tstrncpy, dest, src, sizeof src + 1, DEST_SIZE);
  runTest(tstrncpy, dest, src, DEST_SIZE, DEST_SIZE);

  runTest(tstrncpy, dest, src, -1, DEST_SIZE);

// runTest(tstrncpy, NULL, src, 0, DEST_SIZE);

  return 0;
}

char* tstrcpy(char* dest, char* src)
{
  char* odest = dest;

  while (*src != '\0')
    *dest++ = *src++;

  *dest = '\0'; return odest;
}

char* tstrncpy(char* dest, char* src, size_t maxlen)
{
  size_t len = 0; char* odest = dest;

  /* Don't alter destination if zero-valued 'maxlen' */
  if (maxlen < 1)
    return odest;

  /* Guard against non NUL-terminated buffer */
  while (*src != '\0' && len++ < maxlen)
    *dest++ = *src++;

  /* NUL-fill buffer remnants (added for Standards-conformance) */
  if (len < maxlen)
    while (len++ < maxlen)
      *dest++ = '\0';

  *dest = '\0'; return odest;
}

void runTest(FP_STRNCPY fp, char* dest, char* src, size_t maxlen,
             size_t destlen)
{
  char* rdest = NULL;

  if (dest == NULL || src == NULL)
    printf("Arguments NULL-valued - test may 'blow up'\n");

  memset(dest, 'X', destlen);

  printf("src: %4x:%s\n", src, src);
  printf("dest: %4x:%s\n", dest, dest);
  printf("maxlen: %u\n", maxlen);
  printf("destlen: %u\n", destlen);

  printf("rdest: %4x:%s\n", rdest,
         rdest == NULL ? "NULL" :
           *rdest == '\0' ? "EMPTY" : rdest);

  rdest = fp(dest, src, maxlen);

  printf("rdest: %4x:%s\n\n", rdest,
         rdest == NULL ? "NULL" :
           *rdest == '\0' ? "EMPTY" : rdest);

  fflush(stdout);

  return;
}

/* -------------------------------------------*/



Relevant Pages

  • Re: Function call evaluation order
    ... char *strcpy(char *dest, char *src) ... dest and src are different objects. ... gave a nice view of "undefined" calling sequence (and that explanation I can ...
    (microsoft.public.vc.language)
  • Re: Function call evaluation order
    ... char *strcpy(char *dest, char *src) ... if the post increments are supposed to be evaluated sequentially the ... dest and src are different objects. ...
    (microsoft.public.vc.language)
  • Re: Function call evaluation order
    ... char *strcpy(char *dest, char *src) ... dest and src are different objects. ... int main ...
    (microsoft.public.vc.language)
  • Re: HLA History
    ... Well, I see that teaching people is in of itself, very dangerous. ... In C std libraries, everything is dest, src notation. ... My first programming language was actually TI-83 Calculator basic. ...
    (alt.lang.asm)
  • Very new student, Hanoi Towers
    ... public static void hanoiTower(int topN, char src, char inter, ... char dest) ... Disc 1 from A to C ...
    (comp.lang.java.help)