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;
}
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.