﻿# Во всём виноват компилятор

Многие программисты очень любят обвинять компилятор в различных ошибках\. Поговорим немного об этом\.

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

Когда программист говорит, что причиной ошибки является компилятор, в 99% случаев, он врёт\. Когда начинается детальное изучение проблемы, то, как правило, причины такие:

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

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

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

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

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

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

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

```cpp
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\(\) тоже работает плохо\. Уверен, что он считает разработчиков компиляторов криворукими созданиями\.

```cpp
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\.

Как я наткнулся на эти ошибки? Очень просто \- я использовал анализатора кода [PVS\-Studio](https://pvs-studio.ru/ru/pvs-studio/)\.