How does C free() work?

Possible Duplicate:
How malloc() and free() work

#include <stdio.h>
#include <stdlib.h>

int * alloc()
{
    int *p = (int *)calloc(5,4);
    printf("%d\n",p);
    return p;
}

int main()
{
 int *p = alloc();

 free(p);
 printf("%d\n",p);
 p[0] = 1;
 p[1] = 2;
 printf("%d %d\n",p[0],p[1]);
}

As to the code segment, I allocate 5 ints,first. And then I free the memory. When I printf p, why does p sill have a value same to the memory address allocated first? And I also can assign value to p[0] and p[1]. Does this mean free() do nothing? Once I allocate memory, I can use later though I have freed it.


ANSWERS:


Free does not do nothing. It releases the memory back to the allocator, so the memory can be used again.

What you are doing invokes undefined behavior. Your program may appear to work, but may crash later on.


free releases the memory at that address. It doesn't change the p variable itself. However, doing anything with p after that point is undefined behavior. It may seem to work if you use it immediately after freeing, but it's still completely wrong, and could cause a crash or worse.

free is implementation-specific. However, on most implementations, it will write to book-keeping data in the heap to note that the memory is now available. For instance, it might mark a particular chunk as unused, or combine the chunk with an adjacent free one.

Note that using %d for a pointer is also undefined.


Memory protection has page-granularity and would require kernel interaction

Memory can only be removed from your program in units of pages, and even that is unlikely to be observed.

calloc(3) and malloc(3) do interact with the kernel to get memory, if necessary. But most implementations of free(3) do not return memory to the kernel1, they just add it to a free list that calloc() and malloc() will consult later in order to reuse the released blocks.

Even if a free() wanted to return memory to the system, it would need at least one contiguous memory page in order to get the kernel to actually protect the region, so releasing a small block would only lead to a protection change if it was the last small block in a page.

So your block is there, sitting on the free list. You may be able to access it just as if it were still allocated. C compiles straight to machine code and without special debugging arrangements there are no sanity checks on loads and stores. Now, if you try and access a free block, the behavior is undefined by the standard in order to not make unreasonable demands on library implementators. There are various things that can go wrong:

  • Sometimes allocators maintain separate blocks of memory, sometimes they use a header they allocate just before or after (a "footer", I guess) your block, but they just might want to use memory within the block for the purpose of keeping the free list linked together. If so, your reading the block is OK, but its contents may change, and writing to the block would be likely to cause the allocator to misbehave or crash.
  • Naturally, your block may be allocated in the future, and then it is likely to be overwritten by your code or a library routine, or with zeroes by calloc().
  • If the block is reallocated, it may also have its size changed, in which case yet more links or initialization will be written in various places.

1. The fact that very few implementations of free() attempt to return memory to the system is not necessarily due to the implementors slacking off. Interacting with the kernel is much slower than simply executing library code, and the benefit would be small. Most programs have a steady-state or increasing memory footprint, so the time spent analyzing the heap looking for returnable memory would be completely wasted. Other reasons include the fact that internal fragmentation makes page-aligned blocks unlikely to exist, and it's likely that returning a block would fragment blocks to either side. Finally, the few programs that do return large amounts of memory are likely to bypass malloc() and simply allocate and free pages anyway.


Technically speaking

 p[0] = 1;
 p[1] = 2;

invoke Undefined Behaviour (which means anything can happen) as you are trying to use the dangling pointer p.

Furthermore to be pedantic even printf("%d\n",p); invokes UB (mismatch of format specifier and the type of argument in printf())


free() is actually freeing the memory. However, it does nothing to the pointer. And, indeed, in C, you can attempt to write to any memory location at all. There is no safety checking (beyond the segfault, which crashes the program if you try to access memory outside your program's region). However, that doesn't mean that attempting to use released and/or uninitialized memory is a good idea. That's a memory bug. You'll grow to hate them. :)


free is defined to return memory allocated by malloc and friends to the system. What actually happens is different on different systems. Following things may happen:

  1. The memory is marked as "free" in memory allocator data structures (you never see those directly)
  2. The memory is overwritten partially by memory allocator data (some of them store internal data inside the free blocks)
  3. The memory is reallocated to some other part of the program - e.g., printf might use some memory for some internal purposes, or may not - depends on implementation.
  4. The memory is returned to the OS and thus becomes inaccessible to the process.

Which of these things actually happens, depends on the implementation of your C library and the state of the system in the precise moment you call free and afterwards. One thing though should be clear - you should never use memory after free was called on it in any way. It may crash, it may not crash, but it's never good.

To catch such cases - usage of memory after free - there are a number of programs. In Linux the most popular is valgrind.


Think logically.

Calling free(ptr), you tell the System, that allocated previously memory referred by ptr is free now.

It means, that the System can use the memory now as it likes to. And believe me, soon or later the System will write its own data to the same address, overwriting your one, or the same thing will do another programm in your multitask Operation System.

You will probably ask why ptr has the same value? Well, the answer is simple: velocity. The System do not know if you are going to assign the ptr with a new valid address right after the free call, or you just will abandon it unused.

In any case it is a good practice assign ptr with a NULL pointer right after the free call:

free(ptr);
ptr = NULL;

Because in another part of your function/module/program you will be able to check:

if(NULL == ptr){
/* ... */
}

By the way, if you you will call free twice on the same address somehow, your program will crash - that is another good reason to make an assignment to NULL after the free call, becase free(NULL) - is a safe operation:

free(ptr);
ptr = NULL; /* try to comment out/put back this line and see what happens */
free(ptr);

In a complex program it can happen.



 MORE:


 ? How are malloc and free implemented in C?
 ? how does dynamic memory allocation work
 ? free function not working in c / objective-c
 ? function to free memory of 1D Array
 ? How operator new knows that memory is allocated
 ? Freeing memory in C or C++
 ? freeing memory using free()
 ? How does C free() work?
 ? What if, memory allocated using malloc is deleted using delete rather than free
 ? C: malloc(), free() and then again malloc() does work same always?