14

The function strncpy() doesn't always null terminate so I want to know what is the best alternative that always null terminates? I want a function that if:

strlen(src) >= n /*n is the number of characters to be copied from source*/

there's no need to add further code like this:

buf[sizeof(buf)-1] = 0; 
dems98
  • 814
  • 3
  • 9
  • 22
  • 3
    Look at [`strncpy_s`](http://en.cppreference.com/w/c/string/byte/strncpy) – Yuriy Ivaskevych Jan 26 '17 at 08:58
  • Have a look at the [reference](http://www.cplusplus.com/reference/cstring/strncpy/). You can use strcpy() or add the character yourself, if you know the n value. destination[n] = '\0' – Carles Jan 26 '17 at 09:00
  • @Carles Yea i know but i want to know a function that automatically appends the null character at the end of the string – dems98 Jan 26 '17 at 09:04
  • Can you be a bit more specific? Maybe a code example will help narrow down what your trying to do. – RoadRunner Jan 26 '17 at 09:10
  • `strncpy` inserts null bytes if the passed maximum size is larger than `strlen` of the passed string. Maybe you can use that. – cadaniluk Jan 26 '17 at 09:10
  • @RoadRunner Does `snprintf` always null terminate? – dems98 Jan 26 '17 at 09:12
  • 3
    `snprintf` always null-terminates unless the buffer size is 0 – M.M Jan 26 '17 at 09:19
  • 3
    Why does it matter if you have to add null termination yourself or if the function does it for you? And even you have such irrational requirements, you can simply wrap in the code inside another function. – Lundin Jan 26 '17 at 09:56
  • 2
    Note that the third argument to `strncpy` is the output buffer size, not the number of characters to be copied – M.M Jan 26 '17 at 10:25
  • 1
    Your requirements are still vague. For example please explain whether this function should handle the case of `strlen(src) < n` (and if so, what you want to happen in that case) – M.M Jan 26 '17 at 10:26
  • @YuriyIvaskevych, should the right usage of strncpy_s() be - strncpy(destString, sizeof(destString), srcString, (sizeof(srcString)>sizeof(destString)?(sizeof(destString)-1):(sizeof(srcString)-1))); to handle all the possible scenarios gracefully - ie 1. when the srcString is greater than destString, then truncate the srcString to the length destString. 2. when the destString is greater than srcString. 3. when both srcString and destString are of same length. 4. when srcString is not NULL terminated. – Darshan L Aug 09 '20 at 14:51

6 Answers6

15

If the length of the string you desire to copy is unknown, you can use snprintf here. This function sends formatted output to str. It acts similarily to sprintf(), but instead does not write more bytes allocated by str. If the resulting string is longer than n-1 characters, then the remaining characters are left out. It also always includes the null terminator \0, unless the buffer size is 0.

This would be a alternative to strncpy() or strcpy(), if you really don't want to use it. However, manually adding a null terminator at the end of your string with strcpy() is always a simple, efficient approach. It is very normal in C to add a null terminator at the end of any processed string.

Here is a basic example of using sprintf():

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

#define SIZE 1024

int main(void) {
    const size_t N = SIZE;
    char str[N];
    const char *example = "Hello World";

    snprintf(str, sizeof(str), "%s", example);

    printf("String = %s, Length = %zu\n", str, strlen(str));

    return 0;
}

Which prints out:

String = Hello World, Length = 11

This example shows that snprintf() copied over "Hello World" into str, and also added a \0 terminator at the end.

Note: strlen() only works on null terminated strings, and will cause undefined behaviour if the string is not null terminated. snprintf() also needs more error checking, which can be found on the man page.

As others have said, this is not an efficient approach, but it is there if you go looking.

Community
  • 1
  • 1
RoadRunner
  • 25,803
  • 6
  • 42
  • 75
  • Small example would be good. We don't want beginners doing something horrible like `snprintf(dst, DST_SIZE, src);` – user694733 Jan 26 '17 at 09:27
  • 2
    snprintf is horrible inefficient. If you only wish to use it to copy strings, you are wasting resources. – Lundin Jan 26 '17 at 09:38
  • @user694733 added a basic example, I'm still not sure why OP doesnt want to use `strcpy()` though. – RoadRunner Jan 26 '17 at 09:43
  • @Lundin You are right, but the OP seemed interested, so I gave an answer. Using `strcpy()` and the example you gave is the better approach. – RoadRunner Jan 26 '17 at 09:45
  • @Lundin snprintf has the advantage of working correctly when the source length is unknown, and the length parameter given is the output buffer size. (OP has since edited question to apparently say that that is not his use case) – M.M Jan 26 '17 at 10:33
  • @M.M OP stated earlier that he simply wants a function which automatically adds the null terminator to a string. I believe if that is the OPs requirement, then `snprintf()` is a valid option. Judging from his question, it doesn't seem efficiency is his highest priority. – RoadRunner Jan 26 '17 at 10:41
  • 1
    @lundin Disagree this is horrible inefficient. Optimizing compilers will see inside the format and can create very efficient code. – chux - Reinstate Monica Jan 26 '17 at 15:20
  • @chux I doubt it. And no matter execution speed, that function will take up a big chunk of program memory, for such a trivial task. – Lundin Jan 26 '17 at 15:51
  • @lundin The compiler is not required to even make a function and a function call. Code need not take up a big chunk of program memory. It just needs to implement the functionality. `sprintf(dest, "%s", from);` and `strcpy(dest, from);` can emit the same code and neither call a function. – chux - Reinstate Monica Jan 26 '17 at 16:11
11

If the behavior you want is a truncating version of strcpy that copies the longest initial prefix of the source string into a buffer of known size, there are multiple options for you:

  • You can write a tailor made function that does the job:

    char *safe_strcpy(char *dest, size_t size, char *src) {
        if (size > 0) {
            size_t i;
            for (i = 0; i < size - 1 && src[i]; i++) {
                 dest[i] = src[i];
            }
            dest[i] = '\0';
        }
        return dest;
    }
    

    Most BSD systems have a function strlcpy(char *dest, const char *src, size_t n); that operates the same. The order of its arguments is confusing as n is usually the size of the dest array, but comes after the src argument.

  • You can use strncat():

    char *safe_strcpy(char *dest, size_t size, char *src) {
        if (size > 0) {
            *dest = '\0';
            return strncat(dest, src, size - 1);
        }
        return dest;
    }
    
  • You can use snprintf() or sprintf(), but it feels like using a hydraulic press to drive in a nail:

    snprintf(dest, size, "%s", src);
    

    Alternately:

    if (size > 0) {
        sprintf(dest, "%.*s", (int)(size - 1), src);
    }
    
  • You can use strlen() and memcpy(), but this is only possible if you know that the source pointer points to a null terminated string. It is also less efficient than both of the above solutions if the source string is much longer than the destination array:

    char *safe_strcpy(char *dest, size_t size, char *src) {
        if (size > 0) {
            size_t len = strlen(src);
            if (len >= size)
                len = size - 1;
            memcpy(dest, src, len);
            dest[len] = '\0';
        }
        return dest;
    }
    

    The inefficiency can be avoided with strnlen() if available on the target system:

    char *safe_strcpy(char *dest, size_t size, char *src) {
        if (size > 0) {
            size_t len = strnlen(src, size - 1);
            memcpy(dest, src, len);
            dest[len] = '\0';
        }
        return dest;
    }
    
  • You could use strncpy() and force null termination. This would be inefficient if the destination array is large because strncpy() also fills the rest of the destination array with null bytes if the source string is shorter. This function's semantics are very counter-intuitive, poorly understood and error-prone. Even when used correctly, occurrences of strncpy() are bugs waiting to bite, as the next programmer, bolder but less savvy, might alter the code and introduce them in an attempt to optimize code he does not fully understand. Play it safe: avoid this function.

Another aspect of this question is the ability for the caller to detect truncation. The above implementations of safe_strcpy return the target pointer, as strcpy does, hence do not provide any information to the caller. snprintf() returns an int representing the number of characters that would have been copied if the target array was large enough, in this case, the return value is strlen(src) converted to int, which allows the caller to detect truncation and other errors.

Here is another function more appropriate for composing a string from different parts:

size_t strcpy_at(char *dest, size_t size, size_t pos, const char *src) {
    size_t len = strlen(src);
    if (pos < size) {
        size_t chunk = size - pos - 1;
        if (chunk > len)
            chunk = len;
        memcpy(dest + pos, src, chunk);
        dest[pos + chunk] = '\0';
    }
    return pos + len;
}

This function can be used in sequences without undefined behavior:

void say_hello(const char **names, size_t count) {
    char buf[BUFSIZ];
    char *p = buf;
    size_t size = sizeof buf;

    for (;;) {
        size_t pos = strcpy_at(p, size, 0, "Hello");
        for (size_t i = 0; i < count; i++) {
            pos = strcpy_at(p, size, pos, " ");
            pos = strcpy_at(p, size, pos, names[i]);
        }
        pos = strcpy_at(p, size, pos, "!");
        if (pos >= size && p == buf) {
            // allocate a larger buffer if required
            p = malloc(size = pos + 1);
            if (p != NULL)
                continue;
            p = buf;
        }
        printf("%s\n", p);
        if (p != buf)
            free(p);
        break;
    }
}

An equivalent approach for snprintf would be useful too, passing pos by address:

size_t snprintf_at(char *s, size_t n, size_t *ppos, const char *format, ...) {
    va_list arg;
    int ret;
    size_t pos = *ppos;

    if (pos < n) {
        s += pos;
        n -= pos;
    } else {
        s = NULL;
        n = 0;
    }
    va_start(arg, format);
    ret = snprintf(s, n, format, arg);
    va_end(arg);

    if (ret >= 0)
        *ppos += ret;

    return ret;
}

passing pos by address instead of by value allows for snprintf_at to return snprintf's return value, which can be -1 in case of encoding error.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
9

As an illustration of having to use an alternative to strncpy(), consider Git 2.19 (Q3 2018) which finds that it is too easy to misuse system API functions such as strcat(); strncpy(); ... these selected functions are now forbidden in this codebase and will cause a compilation failure.

That patch does list several alternatives, which makes it relevant for this question.

See commit e488b7a, commit cc8fdae, commit 1b11b64 (24 Jul 2018), and commit c8af66a (26 Jul 2018) by Jeff King (peff).
(Merged by Junio C Hamano -- gitster -- in commit e28daf2, 15 Aug 2018)

banned.h: mark strncpy() as banned

The strncpy() function is less horrible than strcpy(), but is still pretty easy to misuse because of its funny termination semantics.
Namely, that if it truncates it omits the NUL terminator, and you must remember to add it yourself. Even if you use it correctly, it's sometimes hard for a reader to verify this without hunting through the code.
If you're thinking about using it, consider instead:

  • strlcpy() if you really just need a truncated but NUL-terminated string (we provide a compat version, so it's always available)
  • xsnprintf() if you're sure that what you're copying should fit
  • strbuf or xstrfmt() if you need to handle arbitrary-length heap-allocated strings.

Note that there is one instance of strncpy in compat/regex/regcomp.c, which is fine (it allocates a sufficiently large string before copying).
But this doesn't trigger the ban-list even when compiling with NO_REGEX=1, because:

  1. we don't use git-compat-util.h when compiling it (instead we rely on the system includes from the upstream library); and
  2. It's in an "#ifdef DEBUG" block

Since it's doesn't trigger the banned.h code, we're better off leaving it to keep our divergence from upstream minimal.


Note: 1+ year later, with Git 2.21 (Q1 2019), the "strncat()" function itself is now also among the banned functions.

See commit ace5707 (02 Jan 2019) by Eric Wong (ele828).
(Merged by Junio C Hamano -- gitster -- in commit 81bf66b, 18 Jan 2019)

banned.h: mark strncat() as banned

strncat() has the same quadratic behavior as strcat() and is difficult-to-read and bug-prone. While it hasn't yet been a problem in Git iself, strncat() found it's way into 'master' of cgit and caused segfaults on my system.


With Git 2.24 (Q4 2019), it uses the explicit form of 'vsprintf' as the banned version of itself, not 'sprintf'.

See commit 60d198d (25 Aug 2019) by Taylor Blau (ttaylorr).
(Merged by Junio C Hamano -- gitster -- in commit 37801f0, 30 Sep 2019)

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
4

As an alternative to the answer that suggested snprintf(): (Note: trouble if n <= 0)

size_t sz = sizeof buf;
/*n is the number of characters to be copied from source*/
int n = (int) sz - 1;
snprintf(buf, sz, "%s", src);

code can use the following precision:

"... the maximum number of bytes to be written for s conversions. ..." C11 §7.21.6.1 4

sprintf(buf, "%.*s", n, src);

It has the subtle advantage in that src need not be a string, just an array of characters.

Another tool for strings.

Community
  • 1
  • 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
3

Use strlcpy() function.

strlcpy() takes the full size of the destination buffer and guarantee NULL-termination if there is room. Read man page for more information.

Roberto Caboni
  • 7,252
  • 10
  • 25
  • 39
msc
  • 33,420
  • 29
  • 119
  • 214
  • 2
    I don't think `strlcpy` is standard and I for one would prefer standard functions here. – cadaniluk Jan 26 '17 at 09:07
  • There's the `strcpy_s` function which is standard as per C11, but compilers need not support it. Still it is more standard than `strlcpy`. And there is no real reason to use either of them... – Lundin Jan 26 '17 at 09:53
  • 1
    I'm pretty sure that you mean `strncpy_s`. But `strncpy_s` is prone for off-by-one errors. Apparently, one has to specify the target buffer size minus one. `strlcpy` is much more natural. – Sven Mar 26 '18 at 10:59
1

The strcpy function always null-terminates. Of course you should include code to prevent buffer overflows, e.g.:

char buf[50];

if (strlen(src) >= sizeof buf)
{
    // do something else...
}
else
    strcpy(buf, src);
M.M
  • 138,810
  • 21
  • 208
  • 365
  • 3
    Not sure why this got downvoted. If you know the size of the buffer it's an obvious alternative. If you don't know the size of the buffer you have no way to do a safe copy anyway. – Andrew Henle Sep 30 '19 at 22:24