Again, C lets you do dumb stuff.
The memory doesn't physically go away just by calling free.
What you're saying by calling free is along the lines of "I've finished with this memory, someone else can use it, and I promise not to use it".
If you go on to break that promise, well you should read my sig.
What you have is a 'use after free' bug.
Code:
$ gcc -g foo.c
$ valgrind ./a.out
==27728== Memcheck, a memory error detector
==27728== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==27728== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==27728== Command: ./a.out
==27728==
==27728== Invalid read of size 4
==27728== at 0x109251: main (foo.c:29)
==27728== Address 0x4a5c090 is 0 bytes inside a block of size 20 free'd
==27728== at 0x483CA3F: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==27728== by 0x10924C: main (foo.c:27)
==27728== Block was alloc'd at
==27728== at 0x483DFAF: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==27728== by 0x109200: main (foo.c:17)
==27728==
10 10==27728==
==27728== HEAP SUMMARY:
==27728== in use at exit: 0 bytes in 0 blocks
==27728== total heap usage: 3 allocs, 3 frees, 1,056 bytes allocated
==27728==
==27728== All heap blocks were freed -- no leaks are possible
==27728==
==27728== For lists of detected and suppressed errors, rerun with: -s
==27728== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Tools like valgrind can help you find out where your mistakes are.