Многие программисты очень любят обвинять компилятор в различных ошибках. Поговорим немного об этом.
А был ли мальчик?
Когда программист говорит, что причиной ошибки является компилятор, в 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)».
Вот так и рождаются легенды о глючных компиляторах и отважных программистах, которые с ними сражаются.
Вывод
Если что-то работает не так, ищите ошибку в своём коде.
Автор: Andrey2008