Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > Во всём виноват компилятор


Автор: Thunderbolt 10.9.2012, 14:29
Многие программисты очень любят обвинять компилятор в различных ошибках. Поговорим немного об этом.

А был ли мальчик?

Когда программист говорит, что причиной ошибки является компилятор, в 99% случаев, он врёт. Когда начинается детальное изучение проблемы, то, как правило, причины такие:
  • выход за границы массива;
  • неинициализированная переменная;
  • опечатка;
  • ошибка синхронизации в параллельной программе;
  • использование не volatile переменной, там, где надо;
  • написан код, приводящий к неопределённому поведению;
  • и так далее.

Многие проходили через исправление подобных ошибок. Много читали о них. Однако это не мешает вновь и вновь обвинять в смертных грехах компилятор. Каждый раз, кажется, что вот теперь-то виноват именно он.

Конечно, компилятор тоже может содержать ошибки. Но если вы не используете экзотический компилятор для микроконтроллера, то такая вероятность очень низкая. За многие годы работы с Visual C++ я только один раз видел, когда он сгенерировал некорректный ассемблерный код.

Небольшая рекомендация

Прежде чем винить компилятор и писать про это в коде или на форуме, проведите детальное расследование. Во-первых, так вы быстрее устраните ошибку в своём коде. Во-вторых, не будете выглядеть глупо в глазах других программистов, которые укажут на ваш ляп.

Что побудило написать меня эту заметку

Сегодня меня крайне позабавил фрагмент кода из проекта ffdshow. Вот он:

Код
TprintPrefs::TprintPrefs(IffdshowBase *Ideci,
                         const TfontSettings *IfontSettings)
{
  memset(this, 0, sizeof(this)); // This doesn't seem to
                                 // help after optimization.
  dx = dy = 0;
  isOSD = false;
  xpos = ypos = 0;
  align = 0;
  linespacing = 0;
  sizeDx = 0;
  sizeDy = 0;
  ...
}


Глядя на комментарий, я представляю, как негодовал программист. Ах, этот несносный компилятор! В Debug-версии все переменные равны 0. В release-версии из-за неработающей оптимизации, в них мусор. Это безобразие! Плохой, плохой компилятор!

Поругав компилятор, программист оставляет обвиняющий комментарий. И пишет ниже код, который по отдельности обнуляет каждый член класса. Победа мужества над силами зла.

И ведь главное, этот человек останется уверенным, что встречал баг в компиляторе. И будет рассказывать, как из-за него страдал.

Если кто-то не понял весь юмор ситуации, то поясню. Функция memset() не работает из-за простейшей ошибки. Третий аргумент вычисляет размер указателя, а вовсе не структуры. Корректный вызов должен выглядеть так: "memset(this, 0, sizeof(*this));". 

Кстати, рядом у этого программиста функция memcpy() тоже работает плохо. Уверен, что он считает разработчиков компиляторов криворукими созданиями.

Код
void Assign(const AVSValue* src, bool init) {
  if (src->IsClip() && src->clip)
    src->clip->AddRef();
  if (!init && IsClip() && clip)
    clip->Release();
  // make sure this copies the whole struct!
  //((__int32*)this)[0] = ((__int32*)src)[0];
  //((__int32*)this)[1] = ((__int32*)src)[1];
  memcpy(this,src,sizeof(this));
}


Из комментариев видно, что он пытался копировать память альтернативными методами. Впрочем, потом оставил всё-таки функцию 'memcpy()'. Возможно она у него работала в 64-битной программе. Там размер указателя равен 8 байт. А именно 8 байт, он и хочет скопировать.

Ошибка опять в третьем аргументе. Должно быть написано "sizeof(*this)".

Вот так и рождаются легенды о глючных компиляторах и отважных программистах, которые с ними сражаются.

Вывод

Если что-то работает не так, ищите ошибку в своём коде.

P.S.

Как я наткнулся на эти ошибки? Очень просто - я использовал анализатора кода http://www.viva64.com/ru/pvs-studio/.

Автор: boostcoder 10.9.2012, 14:43
жуть какая smile 
как его код вообще приняли?!

Автор: xvr 11.9.2012, 11:17
Самая главная ошибка любого компилятора в том, что он компилирует то, что программист написал, а не то, о чем он думал  smile 

Автор: borisbn 11.9.2012, 14:52
Кстати, по поводу memcpy. Недавно нарвался у себя в коде на такую ошибку: в шаблонном классе есть такой код
Код
memcpy( & m_outData[ count ], &v[ 0 ], v.size() );

где m_outData - указатель на T, v - vector< T >.
пока T у меня был unsigned char всё работало...
На самом деле нужно было v.size() * sizeof( T ), хотя ещё лучше каким нибудь std::copy, но суть не в этом.
PVS-Studio ничего в этой строке не увидел. Я ни в коем случае не утверждаю что должен был, но м.б. стОит подумать, что можно сделать в такой ситуации ?

Автор: Dem_max 11.9.2012, 17:34
Да в принципе такой код memset(this, 0, sizeof(*this)); тоже страшен сам по себе.

Автор: bsa 11.9.2012, 22:17
Dem_max, согласен. При использовании memset в тело класса следует добавить комментарий большими буквами: //WARNING: Use only POD types here because memset used in constructor.

Автор: borisbn 11.9.2012, 23:38
Вопрос к Андрею Карпову: это - http://forum.vingrad.ru/forum/topic-356293.html - UB, ошибка компилятора или всё штатно по стандарту ?

Автор: boostcoder 12.9.2012, 08:15
Цитата(borisbn @  11.9.2012,  14:52 Найти цитируемый пост)
PVS-Studio ничего в этой строке не увидел. Я ни в коем случае не утверждаю что должен был, но м.б. стОит подумать, что можно сделать в такой ситуации ?

присоединяюсь к вопросу.

Автор: Thunderbolt 12.9.2012, 11:10
Цитата(borisbn @ 11.9.2012,  23:38)
Вопрос к Андрею Карпову: это - http://forum.vingrad.ru/forum/topic-356293.html - UB, ошибка компилятора или всё штатно по стандарту ?

Тут всё штатно. Просто у разработчиков странные и неправильные представления о числах с плавающё точкой. Нодо многое о них знать, чтобы правильно с ними работать. Вот я бы не сказал, что умею. Там масса тонкостей.

Ещё немного на эту тему:

http://www.viva64.com/ru/b/0074/

http://www.drdobbs.com/cpp/are-you-sure-that-your-program-works/240006889

Добавлено через 3 минуты и 47 секунд
Цитата(borisbn @ 11.9.2012,  14:52)
Я ни в коем случае не утверждаю что должен был, но м.б. стОит подумать, что можно сделать в такой ситуации ?

Да, про такой паттерн PVS-Studio не знает. Записал пример в список улучшений. Спасибо.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)