10

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;
}
Chris Leyva
  • 3,478
  • 1
  • 26
  • 47
Zack
  • 385
  • 2
  • 3
  • 21
  • 3
    Yes, this leaks memory. – Fred Foo Dec 20 '13 at 21:09
  • 3
    How could it work if it didn't allocate more memory? Where would it put all the duplicates? – Barmar Dec 20 '13 at 21:10
  • 1
    @Barmar The program takes no inputs and produces no outputs, so how it could work without allocating memory could be like this `int main() { return 0; }`. A compiler is permitted to do this optimization, even. – Kaz Dec 20 '13 at 21:19
  • 1
    @Kaz I meant "how could strdup work in any other way?" – Barmar Dec 20 '13 at 21:19
  • You're right Barman. But is there any function that I could use to replace the second call of x() ? Without doing, myself, the reallocation and after that the copy of the new string to the same pointer? Basically, what I'm asking is if there is a function like strdup but with realloc instead of malloc – Zack Dec 20 '13 at 21:24
  • 1
    ^ I added that to the answer. – Kaz Dec 20 '13 at 21:46

3 Answers3

13

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.


* It's possible to have a semantic memory leak with objects that the program hasn't lost a reference to. For instance, suppose that a program keeps adding information to a list which is never used for any purpose and just keeps growing.

Kaz
  • 55,781
  • 9
  • 100
  • 149
  • Thanks for your excelent answer, I learned alot from it! :) I understand that this is the best implementation and I see why, but if I was forced to call the function as strealloc (&test, "etc"),how should I implement it in this case? I'm guessing that I should create a new pointer inside the function, allocate memory for it and after copying the string, if everything went well, only then I would (thus not affect origptr in any way if the memory allocation went wrong) do something like *origptr = newptr (and before this free(*origptr). Please let me know if any of this makes sense. – Zack Dec 20 '13 at 23:05
  • 1
    Whether to use `&test` and update the pointer in place or whether to return the new value is just an interface design detail. If a memory allocation error occurs, you 1) need to know and 2) perhaps don't want to lose the old pointer. So the value-returning interface is better in this regard. It doesn't really change the implementation; we can still use `realloc` if the function is called as `strealloc(&test, "etc")`. It might be a good idea to also return an `int` value in this case: `1` if it works, `0` if it fails. – Kaz Dec 21 '13 at 01:29
  • Is this a good implementation in this case? void *strealloc(char **origptr, char *strdata) { size_t nbytes = strlen(strdata) + 1; char *newptr = malloc(nbytes); if (newptr != NULL) { memcpy(newptr, strdata, nbytes); free(*origptr); *origptr = newptr; } return newptr; } – Zack Dec 21 '13 at 14:16
8

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);
Barmar
  • 741,623
  • 53
  • 500
  • 612
2

Consider the following definition for strdup:

#include <string.h>
char *strdup(const char *string);

strdup reserves storage space for a copy of string by calling malloc. The string argument to this function is expected to contain a null character (\0) marking the end of the string. Remember to free the storage reserved with the call to strdup.

You must free the string yourself.

Fiddling Bits
  • 8,712
  • 3
  • 28
  • 46