Многие программисты очень любят обвинять компилятор в различных ошибках. Поговорим немного об этом.
Когда программист говорит, что причиной ошибки является компилятор, в 99% случаев, он врёт. Когда начинается детальное изучение проблемы, то, как правило, причины такие:
Многие проходили через исправление подобных ошибок. Много читали о них. Однако это не мешает вновь и вновь обвинять в смертных грехах компилятор. Каждый раз, кажется, что вот теперь-то виноват именно он.
Конечно, компилятор тоже может содержать ошибки. Но если вы не используете экзотический компилятор для микроконтроллера, то такая вероятность очень низкая. За многие годы работы с Visual C++ я только один раз видел, когда он сгенерировал некорректный ассемблерный код.
Прежде чем винить компилятор и писать про это в коде или на форуме, проведите детальное расследование. Во-первых, так вы быстрее устраните ошибку в своём коде. Во-вторых, не будете выглядеть глупо в глазах других программистов, которые укажут на ваш ляп.
Сегодня меня крайне позабавил фрагмент кода из проекта ffdshow. Вот он:
Глядя на комментарий, я представляю, как негодовал программист. Ах, этот несносный компилятор! В Debug-версии все переменные равны 0. В release-версии из-за неработающей оптимизации, в них мусор. Это безобразие! Плохой, плохой компилятор!
Поругав компилятор, программист оставляет обвиняющий комментарий. И пишет ниже код, который по отдельности обнуляет каждый член класса. Победа мужества над силами зла.
И ведь главное, этот человек останется уверенным, что встречал баг в компиляторе. И будет рассказывать, как из-за него страдал.
Если кто-то не понял весь юмор ситуации, то поясню. Функция memset() не работает из-за простейшей ошибки. Третий аргумент вычисляет размер указателя, а вовсе не структуры. Корректный вызов должен выглядеть так: «memset(this, 0, sizeof(*this));».
Кстати, рядом у этого программиста функция memcpy() тоже работает плохо. Уверен, что он считает разработчиков компиляторов криворукими созданиями.
Из комментариев видно, что он пытался копировать память альтернативными методами. Впрочем, потом оставил всё-таки функцию 'memcpy()'. Возможно она у него работала в 64-битной программе. Там размер указателя равен 8 байт. А именно 8 байт, он и хочет скопировать.
Ошибка опять в третьем аргументе. Должно быть написано «sizeof(*this)».
Вот так и рождаются легенды о глючных компиляторах и отважных программистах, которые с ними сражаются.
Если что-то работает не так, ищите ошибку в своём коде.
А был ли мальчик?
Когда программист говорит, что причиной ошибки является компилятор, в 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)».
Вот так и рождаются легенды о глючных компиляторах и отважных программистах, которые с ними сражаются.
Вывод
Если что-то работает не так, ищите ошибку в своём коде.
