Because of my limited rating, I cannot yet comment on posts by others, so using "answer" while this is not answer but comment.
The answer/ comment above by Eric Postpischil needs to be peer reviewed.
I don't agree or understand his reasoning.
" ... When an object is freed, although the bytes in the pointer do not change, the data in those structures may change, and then attempting to use the pointer can fail in various ways. Notably, attempting to print the value can fail because the .."
Note the prototype of standard C free() call : void free(void *ptr). At the place of invocation, the ptr value will stay the same before and after the free call - it is passed by value.
To "use" the pointer will/may fail in various ways if by use we mean deference it after the free() call. Printing the value of the pointer does not deference it.
Also printing the (value of) the pointer with a call to printf using %p shall be fine even if pointer value representation is implementation defined.
"*...However, since that rule exists, even less exotic C implementations may take advantage of it for optimization, so a C compiler can implement free(x); printf("%p", (void *) x); equivalently to free(x); printf("%p", (void ) NULL);, for example. ..."
Even if it (compiler) do so, I cannot easily see what would this optimize, a call to a special minimized printf with a value 0 for pointer?
Also consider something like:
extern void my_free(void* ptr); //defined somewhere..
my_free( myptr );
printf("%p", myptr );
It would have nothing to optimize as you suggest here, and it(compiler) could deduce nothing about the value of myptr.
So no, I cannot agree with your interpretation of the C standard at this stage.
(my)EDIT:
Unless, with some 'exotic' implementations with a memory pointer being implemented as a struct, and it's cast to/from "user"s void*.. Then the user's heap value would be invalid or null when trying to print it..
But then every C call that accepts void* as a pointer to a memory would need to differentiate which void* points to heap which doesn't - say memcpy, memset , - and this then gets quite busy ...