C++: Хроники Дурки🚑
853 subscribers
4 photos
41 links
Очень люблю C++, но это скорее уже стокгольмский синдром.
Постоянно нахожу способы стрельнуть себе в ногу.
Download Telegram
Please open Telegram to view this post
VIEW IN TELEGRAM
Первым постом в этом канале станет тот, с которого зародилось название канала.

Долгие разборы "военного синуса" привели меня к квинтессенции проблем, после которых я сумел сконструировать несколько абсолютно отвратительных примеров выстрелов себе в ногу.



#include <iostream>

#include <cmath>
double sin(std::string x) { return 1.5; }

int main() {
std::cout << "string: " << std::sin(std::string{"0.5"}) << std::endl;
}



Вот такой код, очевидно, не скомпилируется.


<source>:7:40: error: no matching function for call to 'sin(std::string)'


Что логично. Мы, конечно, объявили синус от строки, но в глобальном пространстве имен. А вызываем std::sin из пространства имен std, и там такого синуса, очевидно, нет.

Но давайте поменяем местами объявление синуса и заголовок cmath. (можем считать, что у нас есть какой-то локальный заголовок, куда такое объявление просочилось)


#include <iostream>

double sin(std::string x) { return 1.5; }
#include <cmath>

int main() {
std::cout << "string: " << std::sin(std::string{"0.5"}) << std::endl;
}



Угадайте что? Правильно: скомпилируется!


Program returned: 0
string: 1.5


А все почему? Правильно, потому что в заголовке cmath (во многих реализациях на linux) есть вот такая строчка:


using ::sin;


Будьте осторожны. Она там такая не одна.
😱6🤯5🔥2👍1
Возвращаемся к нашему военному синусу.

Любимый пример, связанный с ним, после которого хочется переехать в комнату с мягкими стенами.


#include <iostream>
#include <cmath>

double
__attribute((weak))
sin(double x) { return 1.5; }

int main() {

std::cout << "double: " << std::sin(double(0.5)) << std::endl;
std::cout << "integer: " << std::sin(int(0)) << std::endl;
}


Эта программа нормально скомпилируется, на всех компиляторах, включая icc.
И выводит ожидаемые


Program returned: 0
double: 0.479426
integer: 0


Да, мы объявили какой-то синус в глобальном неймспейсе, который выдает какую-то дичь. Но мы вызываем синусы из стандартной библиотеки, значит все должно быть хорошо.

Ну да ладно, давайте ничего не заменим, только закомментируем последнюю строчку.


#include <iostream>
#include <cmath>

double
__attribute((weak))
sin(double x) { return 1.5; }

int main() {

std::cout << "double: " << std::sin(double(0.5)) << std::endl;
// std::cout << "integer: " << std::sin(int(0)) << std::endl;
}


Что поменяется? Внезапно, поменяется поведение функции в предыдущей строки.


Program returned: 0
double: 1.5


Как так? Почему эта строчка повлияет на поведение предыдущей? А вот, особенности порядка применения оптимизаций icc.

Добро пожаловать к нам в дурку.
😁9🔥6🤬31
Микропостик.

Угадайте, что делает вот такой код?

union S {
long long int number;
bool yes : sizeof(long long int);
};


Вот тут можно посмотреть ответ.


Этот код проверяет, является ли число нечетным.
#include <iostream>

union is_odd {
long long int number;
bool yes : sizeof(long long int);
};

int main() {
std::boolalpha(std::cout);
for (long long int i = -10; i < 10; ++i) {
std::cout << i << " is odd? "
<< is_odd{ .number = i }.yes
<< "\n";
}
}



Вывод

-10 is odd? false
-9 is odd? true
-8 is odd? false
-7 is odd? true
-6 is odd? false
-5 is odd? true
-4 is odd? false
-3 is odd? true
...



Только способом из дурки.
😁103🤯1
Дисклеймер: я не претендую на невероятною новизну фактов. Я просто показываю всем известные примеры, на которые натыкаюсь раз за разом, раз за разом....

Вот еще один прекрасный пример такой гадости.


#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; //
6🤯5🔥4👍1