Seems like you missed the whole point of the article, which was that an abstract "pointer" is not the same thing as the bit field held in some register or memory location, any more than a Java Object is.
Nothing in this article is wrong. What adds to your knowledge of C is that certain operations on C pointers that would make sense if they were just bit pattern are actually undefined behavior. Undefined behavior may lead to subtle bugs due to how compilers do optimizations.
> I don't see how [. . .] is undefined behaviour.
This is a perfectly meaningless sentence. "Undefined behavior" doesn't mean "things which don't make sense in a particular implementation", it literally means behavior not defined by the spec. The spec declines to define the meaning of p==q when p and/or q is a freed pointer. There's nothing to "see" here: it's just not defined.
#include <stdio.h>
int main(void)
{
int x, y;
int *p = &x + 1;
int *q = &y;
printf("%p %p %d\n", p, q, p == q);
return 0;
}
prints 0x7f7fffffdafc 0x7f7fffffdafc 0