Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

strdup and memory leaking

Does strdup allocate another memory zone and create another pointer every time?

For example: does the following code result in a memory leak?

void x(char** d, char* s){
    *d = strdup(s);
}

int main(){
    char* test = NULL;
    x(&test, "abcd");
    x(&test, "etc");
    return 0;
}
like image 623
Zack Avatar asked Dec 20 '13 21:12

Zack


2 Answers

Yes, the program leaks memory because it allocates objects and then loses references to them.

The first time this happens is in the line:

 x(&test, "etc");

The variable test holds the one and only copy of a pointer that was allocated in a previous call to x. The new call to x overwrites that pointer. At that point, the pointer leaks.

This is what it means to leak memory: to lose all references to an existing dynamically allocated piece of storage.

The second leak occurs when the main function returns. At that point, the test variable is destroyed, and that variable holds the one and only copy of a pointer to a duplicate of the "etc" string.

Sometimes in C programs, we sometimes not care about leaks of this second type: memory that is not freed when the program terminates, but that is not allocated over and over again in a loop (so it doesn't cause a runaway memory growth problem).

If the program is ever integrated into another program (for instance as a shared library) where the original main function becomes a startup function that could be invoked repeatedly in the same program environment, both the leaks will turn into problems.

The POSIX strdup function behaves similarly to this:

char *strdup(const char *orig)
{
   size_t bytes = strlen(orig) + 1;
   char *copy = malloc(bytes);
   if (copy != 0)
     memcpy(copy, orig, bytes);
   return copy;
}

Yes; it allocates new storage each time.

If you have a garbage collector (such as Boehm) in your C image, then it's possible that the leaked storage is recycled, and so strdup is able to re-use the same memory for the second allocation. (However, a garbage collector is not going to kick in after just one allocation, unless it is operated in a stress-test mode for flushing out bugs.)

Now if you want to actually reuse the memory with realloc, then you can change your x function along these lines:

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

void *strealloc(char *origptr, char *strdata)
{
    size_t nbytes = strlen(strdata) + 1;
    char *newptr = (char *) realloc(origptr, nbytes); /* cast needed for C++ */
    if (newptr)
      memcpy(newptr, strdata, nbytes);
    return newptr;
}

(By the way, external names starting with str are in an ISO C reserved namespace, but strealloc is too nice a name to refuse.)

Note that the interface is different. We do not pass in a pointer-to-pointer, but instead present a realloc-like interface. The caller can check the return value for null to detect an allocation error, without having the pointer inconveniently overwritten with null in that case.

The main function now looks like:

int main(void)
{
    char *test = strealloc(NULL, "abcd");
    test = strealloc(test, "etc");
    free(test);
    return 0;
}

Like before, there is no error checking. If the first strealloc were to fail, test is then null. That doesn't since it gets overwritten anyway, and the first argument of strealloc may be null.

Only one free is needed to plug the memory leak.

like image 174
Kaz Avatar answered Oct 07 '22 20:10

Kaz


Yes, it allocates memory and leaks if you don't free it. From the man page:

The strdup() function returns a pointer to a new string which is a duplicate of the string s. Memory for the new string is obtained with malloc(3), and can be freed with free(3).

new_s = strdup(s) is essentially equivalent to:

new_s = malloc(strlen(s)+1);
strcpy(new_s, s);
like image 22
Barmar Avatar answered Oct 07 '22 21:10

Barmar