I’ve a question about the memory management in C (and GCC 4.3.3 under Debian GNU/Linux).
According to the C Programming Language Book by K&R, (chap. 7.8.5), when I free a pointer and then dereference it, is an error. But I’ve some doubts since I’ve noted that sometimes, as in the source I’ve pasted below, the compiler (?) seems to work according a well-defined principle.
I’ve a trivial program like this, that shows how to return an array dynamically allocated:
#include <stdio.h>
#include <stdlib.h>
int * ret_array(int n)
{
int * arr = (int *) malloc(10 * sizeof(int));
int i;
for (i = 0; i < n; i++)
{
arr[i] = i*2;
}
printf("Address pointer in ret_array: %p\n", (void *) arr);
return arr;
}
int * ret_oth_array(int n)
{
int * arr = (int *) malloc(10 * sizeof(int));
int i;
for (i = 0; i < n; i++)
{
arr[i] = i+n;
}
printf("Address pointer in ret_oth_array: %p\n", (void *) arr);
return arr;
}
int main(void)
{
int *p = NULL;
int *x = NULL;
p = ret_array(5);
x = ret_oth_array(6);
printf("Address contained in p: %p\nValue of *p: %d\n", (void *) p, *p);
free(x);
free(p);
printf("Memory freed.\n");
printf("*(p+4) = %d\n", *(p+4));
printf("*x = %d\n", *x);
return 0;
}
If I try to compile it with some arguments: -ansi -Wall -pedantic-errors, it doesn’t raises errors or warning. Not only; it also runs fine.
Address pointer in ret_array: 0x8269008
Address pointer in ret_oth_array: 0x8269038
Address contained in p: 0x8269008
Value of *p: 0
Memory freed.
*p+4 = 8
*x = 0
*(p+4) is 8 and *x is 0.
Why does this happen?
If *(p+4) is 8, shouldn’t *x be 6, since the first element of the x-array is 6?
Another strange thing happens if I try to change the order of the calls to free.
E.g.:
int main(int argc, char * argv[])
{
/* ... code ... */
free(p);
free(x);
printf("Memory freed.\n");
printf("*(p+4) = %d\n", *(p+4));
printf("*x = %d\n", *x);
return 0;
}
In fact in this case the output (on my machine) will be:
*p+4 = 8
*x = 142106624
Why does the x pointer is really “freed”, while the p pointer is freed (I hope) “differently”?
Ok, I know that after freeing memory I should make the pointers to point to NULL, but I was just curious 😛
free()(andmalloc()) are not from gcc. They come from the C library, which on Debian is usually glibc. So, what you are seeing is glibc’s behavior, not gcc’s (and would change with a different C library, or a different version of the C library).I particular, after you use
free()you are releasing the memory blockmalloc()gave you. It’s not yours anymore. Since it is not supposed to be used anymore, the memory manager within glibc is free to do whatever it wants with the memory block, including using parts of it as its own memory structures (which is probably why you are seeing its contents change; they have been overwritten with bookkeeping information, probaly pointers to other blocks or counters of some sort).There are other things that can happen; in particular, if the size of your allocation was large enough, glibc can ask the kernel for a separate memory block for it (with
mmap()or similar calls), and release it back to the kernel during thefree(). In that case, your program would crash. This can in theory also happen in some circunstances even with small allocations (glibc can grow/shrink the heap).