Nothing. It has a void result, which means it returns no result, ever.
i.e how can we be sure that free has freed the
exact amount of memory we allocated?
You cannot, and it many implementations it does not. In many
implementations it returns -more- than the exact amount of
memory you allocated.
In many implementations a size_t before the space allocated is used to
keep track of the size of the block (notice you don't specify how much
memory to free, which implies something is keeping track of the size
that was allocated.) size_t does not, however, generally meet the
alignment requirements imposed on malloced memory (which has to return
memory suitable for allocation to double or long double or long long),
so it is not uncommon for there to be some bytes of unused padding
before the block. When you free() the memory, not only the memory
itself but these common (but not universal) overhead bytes get
released for reuse -- so the released size often does not match
the memory size explicitly allocated.
Now, the implementation may promptly seize some of that released
memory to keep track of the free space...
How can we be sure that there is no
memory tagged in the machine when we quit the C application?
You are, at most, responsible for free()ing only what you allocated.
Anything else is up to the implementation to deal with on its own.
The question is really only relevant on a non-"hosted" implementation.
Any "hosted" implementation is going to automatically recover all
process memory, whether automatic or call stack or jump stack or
file buffer or static or string literals or malloc() or
calloc() or executable code or whatever. If it failed to do that,
then when a program bombed (e.g., invoked one of the undefined
or forbidden behaviours) the memory would be lost and over time
all memory would get chewed up, requiring a reboot. So hosted
implementations do all the cleanup work anyhow.
Non-hosted implementations are less well defined -- what would
it mean for your wristwatch to return all its allocated
memory at the end of the program, considering that the program
is never supposed to end?
Usually, at the implementation level, it is trivial to deallocate
all allocated memory -- just a matter of re-initializing a
CPU register or two and scribbling a "all memory is available"
message into the space the implementation uses to keep track of
allocated memory.
Probably all that should really be of concern to you is the
matter of whether the implementation gives back the free()'d
memory to the operating system. The answer to that is multifold:
a) It usually doesn't, because memory is obtained from the
operating system in "pages", and the operating system can only
accept returning complete pages at the best of times -- so if you
allocate 1000 bytes then 10 bytes then free the 1000 bytes,
the implmentation probably could not turn that memory back over
to the OS because the 10 byte chunk that is still in use is
probably on the same system memory page.
b) It would be possible to go through the non-trivial trouble of
noticing that a complete system memory page has been reclaimed (after
a series of malloc() and free() operations, but many operating
are only able to accept returning of pages at the -end- of
allocated memory, not in the middle of an address space, so
it is common for implmentations to never give back memory
obtained from the OS until the program ends.
c) Some operating systems provide (system-specific) facilities
for allocating memory pages that can later be released under
program control. The standard C library malloc() and free()
will not touch this distinct memory... though the OS may provide
extensions to malloc() and free() that allow this kind of
memory to participate in malloc() and free()-type of operations.
d) Operating systems with virtual memory capabilities usually
keep track of which physical memory has been given to a process at
the process-table level, which is outside of user control. When the
process exits, the operating system can be sure of recovering all
the memory by just releasing its hold on those virtual <-> physical
memory bindings, not caring what was formally in those pages
e) non-"hosted" implementations with virtual memory capabilities
deal with memory however they want, and it is unlikely to be useful
to worry about the details of free(). Non-hosted implementations
might not -have- a malloc() or free() as such, as garbage collection
tends to require non-deterministic time, but non-hosted implementations
often have hard real-time requirements.