In addition to the other replies, you could consider using Boehm's conservative garbage collector, then you would use GC_mallocinstead of malloc and you won't do any free (even if, when absolutely certain that a memory zone becomes useless and unreachable, you could explicitly GC_free it).
But the point of C is that you are not allowed to use a free-d data zone. Doing this is undefined behavior, and anything could happen.
As to the question "why can you sometimes still visit an invalid address" (inside a free-d zone), the answer is implementation specific. In practice, on Linux, acquiring memory from the kernel is done thru the mmap(2) (or sometimes sbrk(2)) and releasing it back is done thru the munmap(2) syscalls, which are somehow expensive operations and deal with multiple of page length (often 4Kbytes). So the malloc implementation tries to avoid doing a lot of mmap and munmap syscalls, so usually manages the free-d memory zones differently, by organizing them so they could be reused by a further malloc without doing any mmap syscall. This explains that the memory zone still exist (even if it is inside malloc internal bookkeeping) and you can access it. But doing so will break the internal invariants of malloc and havoc will happen later.
BTW, an address is valid if it is inside the address space, and you can query the address space of process 1234 by reading (sequentially) the /proc/1234/maps file (a pseudo file with some specified textual content on Linux). From inside the process read /proc/self/maps. Try the command cat /proc/self/maps which shows the address space of the process running that cat command to understand more. See proc(5) man page.
Try to strace your simple program to understand what syscalls it is doing.