Copying n characters with strncpy is more efficient in C

I am wondering if there is a cleaner and more efficient way to do the following strncpy , given the number of max characters. I feel like overdoing it.

 int main(void) { char *string = "hello world foo!"; int max = 5; char *str = malloc (max + 1); if (str == NULL) return 1; if (string) { int len = strlen (string); if (len > max) { strncpy (str, string, max); str[max] = '\0'; } else { strncpy (str, string, len); str[len] = '\0'; } printf("%s\n", str); } return 0; } 
+2
c string malloc strncpy
May 03 '12 at 4:27
source share
6 answers

I would not use strncpy for this at all. At least if I understand what you're trying to do, I would probably do something like this:

 char *duplicate(char *input, size_t max_len) { // compute the size of the result -- the lesser of the specified maximum // and the length of the input string. size_t len = min(max_len, strlen(input)); // allocate space for the result (including NUL terminator). char *buffer = malloc(len+1); if (buffer) { // if the allocation succeeded, copy the specified number of // characters to the destination. memcpy(buffer, input, len); // and NUL terminate the result. buffer[len] = '\0'; } // if we copied the string, return it; otherwise, return the null pointer // to indicate failure. return buffer; } 
+6
May 03 '12 at 4:35 a.m.
source share

First, for strncpy: "No null character is implicitly added to the end of the destination, so the assignment will only be null-terminated if the length of the C string in the source is less than zero."

We use memcpy () because strncpy () checks every byte at 0 on each copy. We already know the length of the string, memcpy () makes it faster.

First calculate the length of the string, then determine what to select and copy

 int max = 5; // No more than 5 characters int len = strlen(string); // Get length of string int to_allocate = (len > max ? max : len); // If len > max, it'll return max. If len <= max, it'll return len. So the variable will be bounded within 0...max, whichever is smaller char *str = malloc(to_allocate + 1); // Only allocate as much as we need to if (!str) { // handle bad allocation here } memcpy(str,string,to_allocate); // We don't need any if's, just do the copy. memcpy is faster, since we already have done strlen() we don't need strncpy overhead str[to_allocate] = 0; // Make sure there a null terminator 
+3
May 03 '12 at 4:34
source share

You can reduce the amount of code:

 int main(void) { char *string = "hello world foo!"; int max = 5; char *str = malloc(max + 1); if (str == NULL) return 1; if (string) { int len = strlen(string); if (len > max) len = max; strncpy(str, string, len); str[len] = '\0'; printf("%s\n", str); } return 0; } 

You cannot do to speed up strncpy() . You can reduce the time by using:

 char string[] = "hello world foo!"; 

and then instead of strlen() use sizeof(string) .

Note that if the maximum size is large and the copied string is small, then the fact that strncpy() writes zero for every unused position in the target string can really slow down.

0
May 03 '12 at 4:31
source share

strncpy() will automatically stop as soon as it reaches NUL; passing max without checking is enough.

0
May 03 '12 at 4:31 a.m.
source share

I believe that this is enough:

 char *str = malloc(max+1); if(! str) return 1; int len = strlen(string); memset(str, 0, max+1); int copy = len > max ? max : len; strncpy(str, string, copy); 
0
May 03 '12 at 4:33 am
source share

You basically invent strlcpy , which was introduced in 1996 - see strlcpy and strlcat - a sequential, secure, string copy and concatenation of an article by Todd Miller and Theo de Raadt. You may not have heard about this because you refused to be added to glibc , called "terribly ineffective BSD shit" supporting glibc, and fought that day even when it is accepted by all other operating systems - see Damien's Secure Portableability document Miller (Part 4: Choosing the Right API).

You can use strlcpy on Linux using the libbsd project (packaged in Debian, Ubuntu and other distributions) or simply copying the source code, the code is easy to find on the Internet (for example, using two links in this answer).

But back to your question about what would be most effective in your case when you are not using the length of the source string, here is my idea based on the strlcpy source from OpenBSD at http://cvsweb.openbsd.org/cgi-bin /cvsweb/src/lib/libc/string/strlcpy.c?rev=1.11 , but without checking the length of the source line, which can be very long, but with the correct ending '\ 0':

 char *d = str; // the destination in your example const char *s = string; // the source in your example size_t n = max; // the max length in your example /* Copy as many bytes as will fit */ if (n != 0) { while (--n != 0) { if ((*d++ = *s++) == '\0') break; } } /* Not enough room in dst, add NUL */ if (n == 0) { if (max != 0) *d = '\0'; /* NUL-terminate dst */ } 

Here is the version of strlcpy at http://cantrip.org/strlcpy.c that uses memcpy:

 /* * ANSI C version of strlcpy * Based on the NetBSD strlcpy man page. * * Nathan Myers <ncm-nospam@cantrip.org>, 2003/06/03 * Placed in the public domain. */ #include <stdlib.h> /* for size_t */ size_t strlcpy(char *dst, const char *src, size_t size) { const size_t len = strlen(src); if (size != 0) { memcpy(dst, src, (len > size - 1) ? size - 1 : len); dst[size - 1] = 0; } return len; } 

Which one will be more efficient, I think, depends on the source string. For very long source lines, strlen can take a lot of time, and if you don't need to know the original length, perhaps the first example will be faster for you.

It all depends on your data, so profiling real data will be the only way to find out.

0
Jun 26 '16 at 6:10
source share



All Articles