There are three areas where variables may be created in a c or c++ program.
- global or static variables are at fixed locations in the executable binary file.
- automatic variables outside of static scope are on the stack
- malloc'ed or calloc'ed variables are on the heap.
free() is the function that releases previously allocated memory on the heap. A pointer to memory on the heap is returned by malloc or similar functions. The only way to read or write that memory is via the pointer. A pointer is an address, and a pointer* is the content pointed to at that address.
In the example shown, there are two variables, defined in Main, and effectively static, and the returned value from Main, which is on the stack. Using the miniumum int, 16 bits, here's a possible memory map. In this map, the instructions start at 0, the stack starts at some non-zero value, (beginning of stack - bos) and grows by incrementing, and a heap starts at the maximum address (...FFFF, aka -1) and grows by decrememeting:
(Remember, MIN_INT is -32768, MAX_INT is 32767... the spec guarantees only 16 bits, signed)
Each byte has an address which is 'n' bits wide - 16, 32 or 64 bits, typically
-1. (start of heap, for example, 16 bit addr: 0xFFFF, 32 bit addr: 0xFFFFFFFF or 64 bit addr: 0xFFFFFFFFFFFFFFFF)
-2. (1st location down from beginning of heap. 0x...FFFE) ptr[ 9 ], at one time
-3. (2nd location down from beginning of heap. 0x...FFFD)
-4. (3rd location down from beginning of heap. 0x...FFFC) ptr[ 8 ], at one time
[snip]
-17. (16th location down from beginning of heap. 0x...FFEF)
-18. (17th location down from beginning of heap. 0x...FFEE) ptr[ 1 ], at one time
-19. (18th location down from beginning of heap. 0x...FFED)
-20 (19th location down from beginning of heap. 0x...FFEC) ptr[ 0 ], at one time
-21. (top of heap, 10 X 16 bit ints down from beginning of heap. 0x...FFEB), at one time
:
A very large range of address on 32 or 64 bit machines...
:
tos: ( Top of stack 0x...tos )
bos + ( sizeof( int ) - 1) End of int returned from Main()
bos: (beginning of stack: above static data ) Start of int returned from Mail()
togs: (top of global/static) End of "ptr"
: (size of a pointer is width of address bus... whatever it takes)
togs-(n-1): (top of global/static - (sizeof( int* ) - 1)) start of "ptr"
(togs-n) : End of "ret"
(togs-n)-1: start of "ret"
(any global stuff the compiler adds for itself, the debugger, etc.)
(end of program code)
(start of program code)
(top of non-program code)
0 (start of non-program code, 0x...0000 )
At run-time, "ptr" and "ret" are probably both start at '0', since they're fixed, static, values, read out of the file the executable binary comes from.
As the program runs, the value of "ptr" changes, first, to point into the heap, at the malloc'ed array of 10 ints: "0x...FFEC"
The call to free() doesn't change the value of ptr, its still "0x...FFEC"
Free'ing "0x...FFEC" is legit and runs without anything funny.
The assignment "ptr = &ret" sets a new value into "ptr", "(togs-n)-1", the start of "ret".
Free'ing "(togs-n)-1" causes an immediate crash because "free" checks the value of "(togs-n)-1" and it is NOT in the valid range for a heap address.
"ret" remains blank, its never set, but since its global/static, it remains at whatever it was when the linker wrote it to disk.