Вот еще один прекрасный пример такой гадости.
#include<iostream>
int main(){
int a, b;
int* p = &a;
int* q = &b + 1;
std::cout << std::hex
<< "p: " << p << "\n"
<< "q: " << q << "\n"
<< std::endl;
}
Мы сделаем указатели на две переменные. Если ставить оптимизацию до
O1 включительно (после O2 начинаются вопросы на gcc) то переменные будут объявлены "по порядку", и это будут одинаковые указатели. Просто сравниваем посимвольно.
clang:
Program returned: 0
p: 0x7ffdfaf2696c
q: 0x7ffdfaf2696c
gcc:
Program returned: 0
p: 0x7ffca3076f0c
q: 0x7ffca3076f0c
Внимание, вопрос! Что будет, если мы сравним два одинаковых указателя?
#include<iostream>
int main(){
int a, b;
int* p = &a;
int* q = &b + 1;
std::cout << std::hex
<< "p: " << p << "\n"
<< "q: " << q << "\n"
<< std::endl;
std::cout << (p == q ? "equal" : "not equal")
<< std::endl;
}
И получаем внезапное:
clang:
Program returned: 0
p: 0x7ffd6bd0d16c
q: 0x7ffd6bd0d16c
equal
gcc:
Program returned: 0
p: 0x7ffe3c48696c
q: 0x7ffe3c48696c
not equal
И на самом деле - никаких противоречий со стандартом. Это просто Unspecified (даже не Undefined) Behavior.
Не буду раскрывать всю подноготную, легко этот пример найти где-нибудь на хабре.
Вопрос другой: вот когда на собеседовании в очередной раз задают вопросы типа "разверни список", или "проверь бинарное дерево на наличие циклов".
Вот что будет, если выдать в ответ собеседующему этот пример, и попросить доказать, что сравнение указателей в случае такого дерева/списка будет вообще работать? Без него же такую задачу не решить?
И посмотреть, как хорошо справится с ответом на этот вопрос тот, кто в очередной раз предложит развернуть список.
Хотя, разумеется, можно написать как-то так
int a, b;
auto p = reinterpret_cast<uintptr_t>(&a);
auto q = reinterpret_cast<uintptr_t>(&b + 1);
std::cout << std::hex << p << "\n" //
<< q << "\n" //
<< std::dec << (p == q) << std::endl; //