In C the space you reserve for dasdos and how you effectively use this data are two separate things.
As the sizeof (char) is always 1, you actually, when calling the realloc, left only 3 bytes reserved for your sample text - as you expected.
But the "printf" function does not know this - when fetching a print string, such as control code "% s" it will print the entire destination string until it finds a \x00
character. If there is none before the end of the allocated area for astrign pdem acotnecer one of two things: your program ends with a segmentatin fault, or, pritnf consumes data from an area of an area where it may be being used for other things within your program. By default, the bytes that you freed with realloc continued within a segment used in your program and these data were not used for anything else - then your printf
worked.
But the great lesson to keep in memte is: in C there is no automatic mechanism that will "stop" access to memory and restrict it to areas you have allocated (either with malloc, or by reserving the dimensions in the statement of variable) - you are responsible for knowing that all functions called by your program are accessing allowed memory areas.
If you want to automatically check for vector sizes, and so on, you will only find this in languages of some level more - Java, Go, Pythn, RUby, etc ... In C, and C ++ is you and the CPU.