user5940189
user5940189

Reputation:

strncpy() alternative - what advatages to each version?

I was thinking of making a strncpy alternative with terminating '\0' for my own use in a header file, and am wondering what of the following approaches would be better.

int copystring(char *dest,char *source,int elements)
{
    int run;
    for(run=0;run<elements-1;run++)//last reserved for'\0'
    {
       dest[run]=source [run];
       if (dest[run]=='\0')
       {
           break;
       }

    }
    dest[elements-1]='\0';//could make conditional but not neccesary.
    return 0;
}

OR

int copystring(char *dest,char *source,int elements)
 {
        strncpy(dest,source,elements-1);
        dest[elements-1]='\0';
        return 0;
  }

The obvious difference to me is that I'm calling one less function with version one, but I was wondering if version two has any advantages, as I don't know the internal workings of strncpy()

As an aside, why does strncpy() take size_t as the final argument? if it's for passing sizeof values, which will only be useful in very limited circumstances, wouldn't an int do just as well?

Upvotes: 2

Views: 983

Answers (1)

Keith Thompson
Keith Thompson

Reputation: 263497

The simplest replacement for strncpy() (if you feel that you need one) is probably this:

dest[0] = '\0';
strncat(dest, source, size);

strncat(), unlike strncpy(), guarantees that the target is properly null-terminated, and doesn't copy extra null bytes when the target is bigger than the source.

But be sure that this behavior is really what you want. If you use strcpy() and the target array isn't big enough, you get undefined behavior. With strncpy or strncat, you get defined behavior: the value is quietly truncated. If that's what you want, great -- but it rarely is. More commonly, truncating data just gives you bad data, and you should detect it and handle it as an error rather than Procusteanly discarding whatever won't fit. (Imaging truncating "rm -rf $HOME/unimportant_directory" to "rm -rf $HOME" before passing it to system().)

As an aside, why does strncpy() take size_t as the final argument? if it's for passing sizeof values, which will only be useful in very limited circumstances, wouldn't an int do just as well?

size_t just makes more sense, since it's the type used to represent sizes and lengths. Using int would require defining (or leaving undefined) the behavior for negative arguments, and could limit the size of the string you could handle (if, say, int is 16 bits and size_t is 32 bits). You can always pass an int value, and it will be implicitly converted to size_t.

Another option is the non-standard strlcpy() function. It's not available on all systems, but you should be able to install it (for example, using the libbsd-dev package for Debian/Ubuntu/... systems) or build it from source.

Incidentally, here's my rant on the topic of strncpy():
http://the-flat-trantor-society.blogspot.com/2012/03/no-strncpy-is-not-safer-strcpy.html

Upvotes: 2

Related Questions