Не согласен, лучший валидатор это компилятор
Это неправда.
Даже если компилятор соблюдает стандарт (что довольно редко является правдой - ни gcc, ни clang на текущий момент полностью С++17 не поддерживают), он уж точно не гарантирует, что код, написанный не по стандарту, работать не будет никак (и это в общем случае невозможно гарантировать). Равно он не гарантирует, что написанный не по стандарту код в следующей версии будет работать так же, как в текущей. И довольно часто происходит, что где-то написали код "ну сейчас же работает", потом компилятор / стандартную библиотеку обновляли, и код работать переставал. Один из известных примеров - в flash в
memcpy исходная и целевая области пересекались. Пока копирование шло простым способом слева направа - всё работало.
Потом порядок поменяли - и всё работать перестало.
Она должна отображать все предупреждения, что несоответствует стандарту.
Только про синтаксис.
На практике компилятор предупреждает про
некоторые моменты, которые в рантайме заведомо приведут к UB. Но не про все.
есть ещё ubsan
Который, безусловно, очень полезная утилита, но никаких гарантий не дает.
мой код со всеми этими опциями компилируется без ошибок и предупреждений, при запуске программа выдает, что должна
А если его сделать совсем чуть-чуть интереснее, то идет разброд и шатание: один и тот же код работает по-разному в зависимости от того, вынесен ли он из main в отдельную функцию, и скомпилирован ли с санитайзером или без
#include <iostream>
using namespace std;
void f(uint64_t* tt, char* tt1) {
uint32_t tt32_1 = *(reinterpret_cast<uint32_t*>(&(tt1[0]))) ;
*tt -= 1;
uint32_t tt32_2 = *(reinterpret_cast<uint32_t*>(&(tt1[0]))) ;
std::cout << tt32_1 << ' ' << tt32_2 << ' ' << (tt32_1 == tt32_2) << '\t';
}
int main()
{
uint64_t tt = 1;
char* tt1 = reinterpret_cast<char*>(&tt);
f(&tt, tt1);
tt = 1;
uint32_t tt32_1 = *(reinterpret_cast<uint32_t*>(&(tt1[0]))) ;
tt -= 1;
uint32_t tt32_2 = *(reinterpret_cast<uint32_t*>(&(tt1[0]))) ;
std::cout << tt32_1 << ' ' << tt32_2 << ' ' << (tt32_1 == tt32_2) << std::endl;
return 0;
}
- clang++ 1 1 1 1 0 0
- clang++ -fsanitize=address 1 1 1 1 0 0
- clang++ -fsanitize=undefined 1 0 0 1 0 0
- g++ 1 1 1 1 0 0
- g++ -fsanitize=address 1 1 1 1 0 0
- g++ -fsanitize=undefined 1 1 1 1 0 0
Хотя вот мне удалось подобрать пример и без изменения на полпути:
#include <iostream>
using std::cin;
using std::cout;
int32_t f() {
uint64_t a = 1;
uint32_t b = 2;
int n;
cin >> n;
uint32_t* p = nullptr;
if (n) {
p = reinterpret_cast<uint32_t*>(reinterpret_cast<char*>(&a));
} else {
p = &b;
}
return *p;
}
int main() {
std::cout << f() << std::endl;
return 0;
}
(вводим 0)
- clang++ 1
- clang++ -fsanitize=address 1
- clang++ -fsanitize=undefined 1
- g++ 2
- g++ -fsanitize=address 1
- g++ -fsanitize=undefined 1
Т.е.
gcc с оптимизацией и без санитайзера решает, что, т.к. мы уж точно не будем разыменовывать uint32_t*, указывающий на uint64_t, то равенство нулю n можно и не проверять.
Заметьте, что санитайзеры не ругаются.