Как видно, указатель — это с точки зрения C++ не просто адрес в памяти, а довольно необычная сущность. В частности, можно как бы считать, что у указателя есть невидимый «тег» — на что именно этот указатель указывает. Эти «теги» никак не показаны в памяти, но при этом учитываются в компиляторных оптимизациях.
Таким образом, даже если хранимые в указателях адреса одинаковые, а «теги» разные, то их сравнение вполне себе может вернуть
false (вообще, это UB сравнивать такие указатели). Иногда похожие проблемы стреляют в неожиданных местах: например в Rust (который основан на LLVM, как и Clang) обнаружилась бага, где два одинаковых целых числа (полученных путем каста указателя в число) оказываются не равны друг другу.Еще по этой же причине невалиден
reinterpret_cast между разными типами: в указателе есть «тег», что он указывает на объект одного типа, но при этом мы по этому же указателю пытаемся прочитать объект другого типа. Возникает UB. Впрочем, для unsigned char и еще нескольких подобных типов есть исключение: по указателю на unsigned char можно обращаться к объекту любого типа и читать его побайтовое представление. Такое исключение иногда приводит к интересной просадке производительности: например, вот.Вообще, тема указателей — вещь очень интересная и непростая :)