String concatenation in C, which method is more efficient?

I came across these two methods to concatenate strings:

A common part:

char* first= "First"; char* second = "Second"; char* both = malloc(strlen(first) + strlen(second) + 2); 

Method 1:

 strcpy(both, first); strcat(both, " "); // or space could have been part of one of the strings strcat(both, second); 

Method 2:

 sprintf(both, "%s %s", first, second); 

In both cases, the contents of both will be "First Second" .

I would like to know which one is more efficient (I need to perform several concatenation operations), or if you know the best way to do this.

+56
performance c string concatenation
Sep 05 '09 at 15:53
source share
10 answers

For readability, I would go with

 char * s = malloc(snprintf(NULL, 0, "%s %s", first, second) + 1); sprintf(s, "%s %s", first, second); 

If your platform supports GNU extensions, you can also use asprintf() :

 char * s = NULL; asprintf(&s, "%s %s", first, second); 

If you are stuck at runtime with MS C, you should use _scprintf() to determine the length of the resulting string:

 char * s = malloc(_scprintf("%s %s", first, second) + 1); sprintf(s, "%s %s", first, second); 

Most likely the fastest solution:

 size_t len1 = strlen(first); size_t len2 = strlen(second); char * s = malloc(len1 + len2 + 2); memcpy(s, first, len1); s[len1] = ' '; memcpy(s + len1 + 1, second, len2 + 1); // includes terminating null 
+71
Sep 05 '09 at 16:12
source share

Don't worry about performance: make your code readable and maintainable. I doubt that the difference between these methods will make a difference in your program.

+24
Sep 05 '09 at 15:56
source share

Here is some kind of madness for you, I really went and measured it. Bloody hell, imagine it. I think I have some significant results.

I used a dual-core P4 running Windows using mingw gcc 4.4, creating "gcc foo.c -o foo.exe -std = c99 -Wall -O2".

I tested method 1 and method 2 from the original post. Initially, malloc was maintained outside the control cycle. Method 1 was 48 times faster than method 2. Honestly, removing -O2 from the build command made the exe result 30% faster (not yet researched).

Then I added malloc and free inside the loop. This slowed method 1 by 4.4 times. Method 2 slowed down 1.1 times.

So, malloc + strlen + free SHOULD NOT dominate the profile in order to avoid sprintf.

Here's the code I used (except for loops, they were implemented using <instead of! =, But this violated the HTML rendering of this post):

 void a(char *first, char *second, char *both) { for (int i = 0; i != 1000000 * 48; i++) { strcpy(both, first); strcat(both, " "); strcat(both, second); } } void b(char *first, char *second, char *both) { for (int i = 0; i != 1000000 * 1; i++) sprintf(both, "%s %s", first, second); } int main(void) { char* first= "First"; char* second = "Second"; char* both = (char*) malloc((strlen(first) + strlen(second) + 2) * sizeof(char)); // Takes 3.7 sec with optimisations, 2.7 sec WITHOUT optimisations! a(first, second, both); // Takes 3.7 sec with or without optimisations //b(first, second, both); return 0; } 
+16
Sep 05 '09 at 18:28
source share
 size_t lf = strlen(first); size_t ls = strlen(second); char *both = (char*) malloc((lf + ls + 2) * sizeof(char)); strcpy(both, first); both[lf] = ' '; strcpy(&both[lf+1], second); 
+6
Sep 05 '09 at 16:14
source share

They should be almost the same. The difference will not matter. I would go with sprintf since it requires less code.

+2
Sep 05 '09 at 15:58
source share

The difference hardly matters:

  • If your lines are small, malloc will drown out the string concatenations.
  • If your lines are large, the time taken to copy the data drowns out the differences between strcat / sprintf.

As other posters noted, this is premature optimization. Focus on developing algorithms and come back to this if profiling shows that this is a performance issue.

However ... I suspect Method 1 will be faster. There are some admittedly small ones --- overhead for analyzing a sprintf format string. And strcat is more likely to be "inline".

+2
Sep 05 '09 at 16:31
source share

sprintf () is designed to handle much more than just strings, strcat () is a specialist. But I suspect that you sweat on trifles. Lines C are fundamentally ineffective in ways that make the differences between the two proposed methods insignificant. Read Joel Spolsky's Back to Basics for details.

This is an example where C ++ is usually better than C. To handle a heavy string using std :: string is likely to be more efficient and certainly safe.

[edit]

[2nd edit] Corrected code (too many iterations in the implementation of line C), timings and conclusion, respectively.

I was surprised by Andrew Bainbridge's remark that std :: string was slower, but he did not publish the full code for this test case. I changed it (synchronization automation) and added the std :: string test. The test was conducted on VC ++ 2008 (native code) with the standard "Release" options (that is, optimized), Athlon dual core, 2.6 GHz. Results:

 C string handling = 0.023000 seconds sprintf = 0.313000 seconds std::string = 0.500000 seconds 

Thus strcat () is now much faster (your movement may vary depending on the compiler and parameters), despite the inherent C-line inefficiency convention, and supports my initial suggestion that sprintf () carries most of the luggage is not required for this goal. However, it remains the least readable and safe, so when performance is not critical, it has few IMO advantages.

I also tested the implementation of std :: stringstream, which was much slower, but it still makes sense for complex formatting of strings.

Fixed code:

 #include <ctime> #include <cstdio> #include <cstring> #include <string> void a(char *first, char *second, char *both) { for (int i = 0; i != 1000000; i++) { strcpy(both, first); strcat(both, " "); strcat(both, second); } } void b(char *first, char *second, char *both) { for (int i = 0; i != 1000000; i++) sprintf(both, "%s %s", first, second); } void c(char *first, char *second, char *both) { std::string first_s(first) ; std::string second_s(second) ; std::string both_s(second) ; for (int i = 0; i != 1000000; i++) both_s = first_s + " " + second_s ; } int main(void) { char* first= "First"; char* second = "Second"; char* both = (char*) malloc((strlen(first) + strlen(second) + 2) * sizeof(char)); clock_t start ; start = clock() ; a(first, second, both); printf( "C string handling = %f seconds\n", (float)(clock() - start)/CLOCKS_PER_SEC) ; start = clock() ; b(first, second, both); printf( "sprintf = %f seconds\n", (float)(clock() - start)/CLOCKS_PER_SEC) ; start = clock() ; c(first, second, both); printf( "std::string = %f seconds\n", (float)(clock() - start)/CLOCKS_PER_SEC) ; return 0; } 
+1
Sep 05 '09 at 17:28
source share

I do not know that in case of two there is real concatenation. Printing them back to back is not concatenation.

Tell me what would be faster:

1) a) copy line A to the new buffer b) copy line B to the buffer c) copy buffer to display the buffer

or

1) copy line A to the output buffer b) copy line b to the output buffer

0
Sep 05 '09 at 16:23
source share
  • strcpy and strcat are much simpler fingerprints than sprintf, which should parse a format string
  • strcpy and strcat are small, so they will usually be built into compilers, saving even one extra additional overhead. For example, in llvm strcat will be embedded using strlen to find the starting position of the copy, and then a simple storage instruction
0
Jun 23 '17 at 14:45
source share

Nothing is terribly effective, since both methods must calculate the length of the string or scan it each time. Instead, since you still evaluate strlen () s of individual lines, put them in variables, and then just strncpy () twice.

-one
Sep 05 '09 at 15:56
source share



All Articles