What is a safe way to join strings in C?

10,275

Solution 1

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

char *join(const char* s1, const char* s2)
{
    char* result = malloc(strlen(s1) + strlen(s2) + 1);

    if (result) // thanks @pmg
    {
        strcpy(result, s1);
        strcat(result, s2);
    }

    return result;
}

This is simple enough to be written in place, especially when you have multiple strings to concatenate.

Note that these functions return their destination argument, so you can write

char* result = malloc(strlen(s1) + strlen(s2) + 1);
assert(result);
strcat(strcpy(result, s1), s2);

but this is less readable.

Solution 2

#include <stdio.h> 

char *a = "hello ";
char *b = "goodbye";
char *joined;

asprintf(&joined, "%s%s", a, b)

Solution 3

There are several problems in this code: 1 - calling strlen on the for loop is a bad idea, it will calculate the string length every iteration, so it is better to call it once before the loop and keep the result in a variable.

2 - The same strlen problem applies to strlen(path_cmp1) inside the loop, call it before the loop and increments its size.

In the end, it is better to simple copy both strings and store those on a dynamic allocated string, like:

char *join_strings(const char* s1, const char* s2)
{
    size_t lens1 = strlen(s1);
    size_t lens2 = strlen(s2);

    //plus 1 for \0
    char *result = malloc(lens1 + lens2 + 1);

    if(result)
    {
        memcpy(result, s1, lens1);
        memcpy(result+lens1, s2, lens2+1);
    }

    //do not forget to call free when do not need it anymore
    return result;
}

Solution 4

There are strcat and strncat for that.

Solution 5

char *path_cmp1 = "/Users/john/";
char *path_cmp2 = "foo/bar.txt";

int firstLength = strlen(path_cmp1);
int secondLength = strlen(path_cmp2);
char *both = malloc(firstLength+secondLength+1);
memcpy(both, path_cmp1, firstLength);
memcpy(both+firstLength, path_cmp2, secondLength+1);
       // this +1 copyes the second string's null-terminator too.
Share:
10,275
Admin
Author by

Admin

Updated on June 06, 2022

Comments

  • Admin
    Admin almost 2 years

    I need to construct a path to a file from two strings. I could use this (not tested, though):

    /* DON'T USE THIS CODE! */
    /* cmp means component */
    char *path_cmp1 = "/Users/john/";
    char *path_cmp2 = "foo/bar.txt";
    unsigned len = strlen(path_cmp1);
    char *path = path_cmp1;
    for (int i = 0; i < strlen(path_cmp2); i++) {
      path[len + i] = path_cmp2[i];
    }
    

    but this could lead to memory corruption I guess. Is there a better way to do this, or is there a function for this in the standard library?

  • pmg
    pmg about 13 years
    +1 ... but if (result) { strcpy; strcat; } and automagically return NULL if malloc fails
  • Fred Foo
    Fred Foo about 13 years
    By "written in place", do you mean join(join("foo", "bar"), "baz")? That would leak to a helluva memory leak. Your second example isn't only ugly, but unsafe due to lack of error checking.
  • Fred Foo
    Fred Foo about 13 years
    Non-standard, but not too hard to implement in terms of snprintf. +1.
  • Admin
    Admin about 13 years
    @pmg I always check for NULL when using malloc.
  • Alexandre C.
    Alexandre C. about 13 years
    @larsman: no, I mean writing the malloc, strcpy and strcat entirely instead of the join function.
  • Bernd Elkemann
    Bernd Elkemann about 13 years
    This code scans s1 3 times for and s2 twice to just find out their lengths. If you want it faster you only need to scan each of them once.
  • Alexandre C.
    Alexandre C. about 13 years
    Yes, there is a performance penalty in writing "clear" code :)
  • Alexandre C.
    Alexandre C. about 13 years
    @eznme: this is clear code which works. After profiling, this can indeed be optimized. I prefer this than hunting off-by-one errors.
  • nass
    nass over 11 years
    @AlexandreC. I am fuzzy about strings; doesn't this malloc need to be freed somewhere (outside the scope of the join function) ?
  • nass
    nass over 11 years
    @AlexandreC. perhaps it's a vital update in your answer then. I can 'see' many newbies using the function as is and forgetting to free the malloc completely.
  • chux - Reinstate Monica
    chux - Reinstate Monica almost 7 years
    Recommend if(result) { } around the two memcpy().