1

I have a question about the following function and about memory allocation.

char    *strjoin(char const *s1, char const *s2)
{
    char    *s3;

    s3 = NULL;
    if (s1 && s2)
    {
        s3 = (char *)malloc(sizeof(char) * (strlen(s1) + strlen(s2) + 1));
        if (!s3)
            return (NULL);
        strcpy(s3, s1);
        strcat(s3, s2);
    }
    return (s3);
}

int main()
{
    char    *s1 = "my favorite animal is";
    char    *s2 = " ";
    char    *s3 = "the nyancat";
    char    *res = strjoin(strjoin(s1, s2), s3);
}

The strjoins doc is as follows:

Allocates (with malloc(3)) and returns a “fresh” string ending with ’\0’, result of the concatenation of s1 and s2. If the allocation fails the function returns NULL.

In the main function, the function calls itself on this line:

char *res = strjoin(strjoin(s1, s2), s3);

Since memory is being allocated in the strjoin(s1, s2) but never assigned to anything, and it is being used in the outer function call, but never freed technically, does that memory just leak and go unused?

Thunderpurtz
  • 189
  • 2
  • 12

1 Answers1

1

Yes, the inner call does leak memory. You can avoid the leak easily by storing the return value in place:

char    *intermediate;
char    *res = strjoin(intermediate = strjoin(s1, s2), s3);
free(intermediate);

or

char    *intermediate = strjoin(s1, s2);
char    *res = strjoin(intermediate, s3);
free(intermediate);

But the memory would be freed by the operating system main function / when exit is called if this is a hosted system. Unless you're programming an embedded device, device driver or an operating system, you're very probably targetting a hosted platform.


Now, depending on whether the compiler is clever or not, it might not actually allocate any memory for that program, because the result is not needed.

Consider the slightly changed program:

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

static inline __attribute__((always_inline)) char *strjoin(char const *s1, char const *s2)
{
    char    *s3; 
    s3 = NULL;

    if (s1 && s2) {
        s3 = malloc(strlen(s1) + strlen(s2) + 1);
        strcpy(s3, s1);
        strcat(s3, s2);
    }

    return s3;
}

int main()
{
    char    *s1 = "my favorite animal is";
    char    *s2 = " ";
    char    *s3 = "the nyancat";
    char    *res = strjoin(strjoin(s1, s2), s3);
}

I've added static inline __attribute__((always_inline)) to the function so that it is seen only within the file scope but not from other .c files (it doesn't have external linkage), and that the function will always be inlined in place; I've also removed the return value checking for malloc, as it seemed to hinder GCC's ability of deducing what will happen here. (It somehow thinks that tracking the NULL status of malloc is relevant).

If you compile this program with highest optimization level with latest GCC trunk the compiler will notice that all of the calculations are useless and compiles a much simpler program:

int main(void) {
}

(see the identical assembly in the middle pane) that will have the same observable behaviour, but which does not call malloc even once.