Until today I lived in belief that calling free()
on memory space releases it for further allocation without any other modifications. Especially, considering th
As others pointed out, you are not allowed to do anything with a free
d pointer (else that is the dreaded undefined behavior, which you should always avoid, see this).
In practice, I recommend never coding simply
free(ptr);
but always coding
free(ptr), ptr=NULL;
(since practically speaking this helps a lot to catch some bugs, except double free
s)
If ptr
is not used after that, the compiler would optimize by skipping the assignment from NULL
In practice, the compiler knows about free
and malloc
(because the standard C libraries headers would probably declare these standard functions with appropriate function attributes -understood by both GCC & Clang/LLVM) so might be able to optimize the code (according to the standard specification of malloc
& free
....), but the implementation of malloc
and free
is often provided by your C standard library (e.g. very often GNU glibc or musl-libc on Linux) so the actual behavior is provided by your libc
(not the compiler itself). Read appropriate documentation, notably free(3) man page.
BTW, on Linux, both glibc
and musl-libc
are free software, so you might study their source code to understand their behavior. They would sometimes obtain virtual memory from the kernel using a system call like mmap(2) (and later release back the memory to the kernel using munmap(2)), but they generally try to reuse previously free
d memory for future malloc
s
In practice, free
could munmap
your memory (notably for big memory malloc
-ated zones) - and then you'll get a SIGSEGV
if you dare dereferencing (later) that free
d pointer, but often (notably for small memory zones) it would simply manage to reuse that zone later. The exact behavior is implementation specific. Usually free
does not clear or write the just freed zone.
You are even allowed to redefine (i.e. re-implement) your own malloc
and free
, perhaps by linking a special library such as libtcmalloc, provided your implementation has a behavior compatible with what the C99 or C11 standard says.
On Linux, disable memory overcommit and use valgrind. Compile with gcc -Wall -Wextra
(and probably -g
when debugging; you might consider also passing -fsanitize=address
to recent gcc
or clang
at least to hunt some naughty bugs.).
BTW, sometimes Boehm's conservative garbage collector might be useful; you'll use (in your whole program) GC_MALLOC
instead of malloc
and you won't care about free
-ing memory.
is there a better explanation?
There is. Dereferencing a pointer after it has been free()
d results in undefined behavior, so the implementation has the permission to do anything it pleases, including the act of tricking you into believing that the memory region has been filled with zeroes.
There's no single definitive answer to your question.
(The rest applies to the blocks retained in the internal memory pool.)
Secondly, there's little sense in filling freed memory with any specific value (since you are not supposed to access it), while the performance cost of such operation might be considerable. Which is why most implementations don't do anything to freed memory.
Thirdly, at debugging stage filling freed memory with some pre-determined garbage value can be useful in catching errors (like access to already freed memory), which is why many debug implementations of standard library will fill freed memory with some pre-determined value or pattern. (Zero, BTW, is not the best choice for such value. Something like 0xDEADBABE
pattern makes a lot more sense.) But again, this is only done in debug versions of the library, where performance impact is not an issue.
Fourthly, many (most) popular implementations of heap memory management will use a portion of the freed block for its internal purposes, i.e. store some meaningful values there. Which means that that area of the block is modified by free
. But generally it is not "zeroed".
And all this is, of course, heavily implementation-dependent.
In general, your original belief is perfectly correct: in the release version of the code a freed memory block is not subjected to any block-wide modifications.
Is free() zeroing out memory?
No. The glibc malloc implementation may overwrite up to four times the size of a pointer of the former user data for internal housekeeping data.
The details:
The following is the malloc_chunk
structure of glibc (see here):
struct malloc_chunk {
INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};
The memory region for user data in an allocated memory chunk begins after the size
entry. After free
is called the memory space where the user data has been may be used for lists of free memory chunks, so the first 4 * sizeof(struct malloc_chunk *)
bytes of the former user data are probably overwritten, hence another value than the former user data value is printed out. It is undefined behaviour. If the allocated block is larger there could be a segmentation fault.
free()
can actually return memory to the operating system and make the process smaller. Usually, all it can do is allow a later call to malloc to reuse the space. In the meantime, the space remains in your program as part of a free-list used internally by malloc
.
free()
does not zero memory as a general rule. It simply releases it for re-used by a future call to malloc()
. Certain implementations may fill the memory with known values but that is purely an implementation detail of the library.
Microsoft's runtime makes good use of marking freed and allocated memory with useful values (see In Visual Studio C++, what are the memory allocation representations? for more information). I have also seen it filled with values that when executed would cause a well-defined trap.