2

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?

  • 1
    What is this accomplishing that `strcopy` doesn't already provide? Additionally why do you need to know the length of a null-terminated `char *source` to copy it? – CollinD Apr 27 '16 at 19:40
  • It has buffer-overflow protection for the dest. i.e I'll be using 'elements' to pass the max destination length. –  Apr 27 '16 at 19:44
  • BTW: `dest[run+1]='\0';` -->> `dest[run]='\0';` (run is already incremented after the loop) – wildplasser Apr 27 '16 at 19:44
  • The first function is simply broken, since it unconditionally copies `elements-1` chars ignoring the fact that `source` can end well before that. – AnT stands with Russia Apr 27 '16 at 19:44
  • @Orangesandlemons Then it sounds like you're reimplementing `strncpy` rather than `strcpy` as you stated. – CollinD Apr 27 '16 at 19:45
  • @CollinD: The spelling is `strncpy` and `strcpy`, not `strncopy` and `strcopy`. – Keith Thompson Apr 27 '16 at 19:46
  • 1
    @KeithThompson ah absolutely, typing too quickly for my own good. `s/copy/cpy/g` pls. `#define strcopy strcpy` `#define strncopy strncpy` – CollinD Apr 27 '16 at 19:46
  • @ant, yes. I've overlooked that, and will adapt. –  Apr 27 '16 at 19:47
  • `strncpy` isn't really a string function at all, because it might not produce a string (nul-terminated), and that it fills destination with zeroes if there's room, in that case writing beyond end of string. It is for filling fixed size char arrays, usually struct fields. – hyde Apr 27 '16 at 19:48
  • Corner case: `copystring(char *dest,char *source,int elements)` (2nd version) fails dramatically when `elements <= 0` – chux - Reinstate Monica Apr 27 '16 at 20:01
  • @CollinD sorry, it was a typo in- I had it right in the title, and have now corrected it in the body –  Apr 27 '16 at 20:02
  • @Orangesandlemons Whoops; sorry if I came off overly pedantic. Makes a lot more sense now. – CollinD Apr 27 '16 at 20:02
  • @chucx yes, but as that would mean I've created a destination array of zero elements, as that's how I plan to use it. –  Apr 27 '16 at 20:06
  • @CollinD not at all, and thanks for pointing it out. –  Apr 27 '16 at 20:06
  • 1
    @Orangesandlemons To be clear: `copystring(dest, source, 0)` calls `strncpy(dest,source,0-1);` which is `strncpy(dest,source,some_very_large_number);` and not `strncpy(dest,source, -1);` – chux - Reinstate Monica Apr 27 '16 at 20:46

1 Answers1

2

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

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • My plan is to continue reading the string until the end is reached (but not into an array), and then return that number as the function return, allowing customised error handling depending on need. Of course, if I need all the characers I will have to allocate dynamicly –  Apr 27 '16 at 19:58
  • 1
    Note: important that `size` does not exceed the size of the space pointed to by `dest`. 2) BTW: good choice between OP's 2 options: neither – chux - Reinstate Monica Apr 27 '16 at 19:58
  • One disadvantage of this is that it would require initialising dest [0] with '\0', while idealy I'd have as little code as possible. I'm still trying to work out a way to dispense of needing to tell my function the size of the destination array in cases where an array has decayed to a pointer etc. –  Apr 27 '16 at 20:11
  • 1
    @Orangesandlemons: You *can't* avoid telling your function the size of the destination array, unless you're willing to allow buffer overruns. – Keith Thompson Apr 27 '16 at 20:13
  • @Keith Thompson I know, but if arrays didn't decay I'd be able to tell it internally, rather than pass it as an argument, hus making a much 'cleaner' function. –  Apr 27 '16 at 20:16