Почему предупреждения компилятора — наши друзья?

Даже если вы исправили все ошибки компилятора и компоновщика, компилятор все еще может посылать вам «предупреждения» (нахальный ублюдок). Эти предупреждения не будут мешать при компиляции вашего кода (только если вы укажете компилятору рассматривать предупреждения как ошибки), и вы могли бы попытаться просто игнорировать их. Однако, есть некоторые веские причины не делать этого. Я также расскажу о некоторых конкретных примерах предупреждений, с которыми вы можете столкнуться, что они означают, и как их исправить.

Найти ошибки перед тестированием

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

assignment in conditional

то это может означать, что вы написали 

if ( x = 5 ) //в этом случае x-су будет присваиваться значение 5, и условие if всегда будет истинно
{
        // тело оператора выбора
}

а то, что вы на самом деле имели в виду

if ( x == 5 )
{
        // тело оператора выбора
}

Вы, наверное, поняли это, когда ваш код всегда входил в условный оператор, но вы точно не знали, почему он это делает. Может быть, х не был установлен должным образом (например, оно мог быть не инициализирован). Самым страшным в этой ситуации является то, что вы никогда не проверяли эту ветвь условного оператора, когда х не был равен пяти. Это означает, что ошибка в коде останется у вас, даже если вам кажется, что вы исправили ее. Таким образом, предупреждение компилятора сообщает вам, что именно неправильно и предупреждает вас о проблемах, которые вы никогда бы не нашли!

Просто для разнообразия, вот еще более убедительный пример поиска ошибок во время тестирования:

if ( y = 0 )
{
   std::cout << "y равен 0";
}

Так как y как правило, не равен нулю, да вообще не важно, какое значение содержит y. После выполнения строки 1 переменной y будет присвоено значение 0. Теперь, так как y = 0, то условие оператора условного выбора оценивается как ложное, причём оно всегда будет ложным. И, если вы не проверите вручную, когда на самом деле y равен 0, то вы не заметите причину, по которой тело оператора выбора if никогда не выполняется.

Ошибки, которые трудно найти при тестировании программы

Компиляторы всегда предупредят вас об ошибках, которые трудно найти во время тестирования программы. Например, компилятор может предупредить вас, что вы используете неинициализированную переменную. Во время тестирования трудно найти объявленную переменную, которая не была предварительно инициализирована, но уже используется в программе. А к чему это может привести? Просто, мы будем думать, что работаем с корректным значением, а на самом деле в переменной содержится «мусор». (Заметим, что  неинициализированная переменная — переменная, которой не было присвоено значение, после объявления.).

Другая коварная ошибка — мы часто забываем вернуть значение из функции. Иногда сложно обнаружить такую ошибку. Чаще всего такая ошибка возникает, если ваша функция имеет несколько ветвлений, и большинство из них возвращает значение,  а одна ветвь нет, и это грубейшая ошибка.

int searchVector(int vector[], int length, int find)
{
        for(int ix = 0; ix < length; ++ix )
        {       
                if ( vector[ix] == find )
                {
                        return ix;
                }
        }
}

Функция searchVector, на первый взгляд, составлена правильно и работает правильно. Ну а если присмотреться, searchVector не имеет возвращаемого значения на случай, когда искомое значение не содержится в массиве, она его не найдет. Эту ошибку трудно определить в дальнейшем тестировании потому, что это редкий случай и потому, что ожидаемые результаты возвращаемого значения могут быть чрезвычайно странными. Например, вы могли бы получить допустимый индекс массива, или вы могли бы получить что-нибудь слишком большое (что более вероятно). То есть в одних случаях программа работает исправно, а в других — нет. В любом случае, проблемы в запущенной программе не будут проявляться в функции.

Как избежать появления возможных ошибок

Итак вы получили несколько советов о том, как справиться с предупреждениями компилятора, вы должны сделать следующее — обнаружить ошибки как можно раньше, и пользоваться инструментами, такими как компилятор, чтобы точно знать где проблемы, прежде чем вы обнаружите что-то странное во время тестирования. Иногда нет необходимости трактовать предупреждения как ошибки, но если вы используете автоматизированные скрипты для создания вашей программы или склонны игнорировать компилятор, то это нужно сделать. Вы должны просмотреть документацию по компилятору, чтобы знать, как именно реализовать эту возможность; на gcc и g++ все, что нужно использовать — это флаг -Werror.

Кроме того, если вы не понимаете, что означают предупреждения компилятора, значит, вам, вероятно, лучше верить, что компилятор сообщает вам что-то ценное. Случалось, что я был убежден, что мой код был правильным и не понимал, почему компилятор предупреждает. Но после анализа предупреждений компилятора, я понял, что сделал незначительную ошибку в логическом выражении, которую было бы почти невозможно исправить во время отладки — было гораздо легче найти ошибку пока код был свеж в моей памяти, а не часы или, возможно, дни спустя, когда я мог бы (если повезет) найти эту ошибку.

Автор: Marienko L.
Дата: 02.10.2012
Поделиться:

Оставить комментарий

Вы должны войти, чтобы оставить комментарий.