When your compiler translates your C code into executable machine code, most of the information is deleted, including type information. Where do you write:
int x = 42;
the generated code simply copies a specific bit pattern into a specific part of the memory (a piece, which can usually be 4 bytes). You cannot say, having studied machine code, that a piece of memory is an object of type int .
Similarly, when you write:
if (mynode->next_node == NULL) { }
the generated code will retrieve a piece of memory with the size of the pointer, dereferencing another piece of memory with the size of the pointer and comparing the result with the system representation of the null pointer (usually all bits are zero). The generated code does not directly reflect the fact that next_node is a member of the structure or something about how the structure was distributed or whether it exists.
The compiler can check many things at compile time, but it does not necessarily generate code to perform checks at runtime. It is up to you as a programmer to avoid mistakes in the first place.
In this particular case, after calling free , mynode has an undefined value. It does not point to any valid object, but there are no requirements for the implementation to do anything with this knowledge. The free call does not destroy the allocated memory, it just makes it available for allocation by future malloc calls.
There are several ways to implement such checks, such as implementation, and throwing a runtime error if you search for a pointer after free . But such checks are not required in C, and they are usually not implemented, because (a) they would be quite expensive, making your program run slower, and (b) the checks cannot catch all the errors.
C is defined so that memory allocation and pointer manipulation will work correctly if your program does everything right. If you make certain errors that can be detected during compilation, the compiler can diagnose them. For example, assigning a pointer value to an integer requires at least a compile-time warning. But other errors, such as dereferencing the free d pointer, cause your program to have undefined behavior. It is up to you as a programmer to avoid these errors in the first place. If you fail, you are on your own.
Of course, there are tools that can help. Valgrind - alone; smart optimizing compilers is different. (Enabling optimization forces the compiler to do more analysis of your code and often allows it to diagnose more errors.) But ultimately, C is not the language that holds your hand. This is a sharp tool - and one that can be used to create safer tools, such as interpreted languages, that do a lot of checking at runtime.