Форум компании ITRM
Информационные технологии

Начало » Общение на свободные темы » Общение на свободные темы » Заметки программиста С++
Заметки программиста С++ [сообщение #443] Чтв, 24 Май 2007 13:32 Переход к следующему сообщения
Денис Шевченко в настоящее время не в онлайне  Денис Шевченко
Сообщений: 53
Зарегистрирован: Март 2005
Географическое положение: Москва
Member
Приветствую всех!

Уж не знаю, сколь много среди участников форума программистов на С и/или С++, но, надеюсь, не я один. Поэтому хочу поделиться маленькими хитростями, полезными в создании программных продуктов.
Все нижесказанное воспринимать как мое личное мнение, однако подискутировать готов.
Заметки программиста С++ [сообщение #444 является ответом на сообщение #443] Чтв, 24 Май 2007 13:37 Переход к предыдущему сообщенияПереход к следующему сообщения
Денис Шевченко в настоящее время не в онлайне  Денис Шевченко
Сообщений: 53
Зарегистрирован: Март 2005
Географическое положение: Москва
Member
Задача:
быстро найти в коде ошибку
...
extern int a;   // Какой-то внешний флаг...
extern int b;   // Какой-то внешний флаг...
...
int main()
{
    if ( a == 0 || b = 2 ) {
        // Сделать что-то ОЧЕНЬ важное!
    }
    ...
}

Re: Заметки программиста С++ [сообщение #445 является ответом на сообщение #443] Чтв, 24 Май 2007 13:51 Переход к предыдущему сообщенияПереход к следующему сообщения
Денис Шевченко в настоящее время не в онлайне  Денис Шевченко
Сообщений: 53
Зарегистрирован: Март 2005
Географическое положение: Москва
Member
Нашли? Ну ладно, вот подсказка: что происходит с флагом b? Проверка? Нет, присваивание! Знак проверки на равенство "==" случайно (а может, и не случайно Evil or Very Mad ) перепутан со знаком присваивания "="! В результате нечто ОЧЕНЬ важное, что должно было произойти, не произойдет.
Как решить эту проблему? Вы скажете, что нужно быть внимательными. Да, это так. Но программисты НЕ всегда внимательны, в силу разных причин.
Есть другой путь, который ГАРАНТИРОВАННО избавляет от продемонстрированной ошибки в БОЛЬШИНСТВЕ случаев! Я сам пользуюсь этим приемом, и вам очень советую!
Вспомним теорию языка. В выражении а = 2 а является lvalue, а 2 - rvalue. Ошибка в приведенном мною примере тем опасна, что компилятор ее не покажет, поскольку синтаксис языка нарушен не был. Я сам накалывался на подобную ошибку и тратил немало времени на ее обнаружение. Способ избежать такой ошибки в БОЛЬШИНСТВЕ случаев прост: поменяйте местами lvalue и rvalue! Тогда получится не a = 2, а 2 = a. Но постойте! Это же ошибка! Константа не может быть lvalue! Мы ведь не можем присвоить что-либо числу 2. Любой компилятор, старый или новый, поддерживающий стандарт С++ или неподдерживающий, сразу покажет эту ошибку.
Значит, если нужно, как в приведенном мною примере, проверить условие b == 2, то если мы напишем 2 == b, то ничего не измениться, проверка условия произойдет по-любому. А вот если теперь я перепутаю (или захочу навредить Evil or Very Mad ) и напишу 2 = b, то присваивания уже НЕ произойдет, компилятор выдаст сообщение об ошибке.
Вот и решение проблемы. Однако я сказал выше, что это работает в БОЛЬШИНСТВЕ случаев, то есть не всегда. Да, это не работает в том лишь случае, если обе проверяемые на равенство переменные являются lvalue, однако это встречается значительно реже. В этом случае нужно быть ПРОСТО внимательным.

[Обновления: Чтв, 24 Май 2007 14:15]

Известить модератора

Re: Заметки программиста С++ [сообщение #491 является ответом на сообщение #443] Вск, 10 Июнь 2007 23:54 Переход к предыдущему сообщенияПереход к следующему сообщения
Денис Шевченко в настоящее время не в онлайне  Денис Шевченко
Сообщений: 53
Зарегистрирован: Март 2005
Географическое положение: Москва
Member
Всем привет!
Еще одним советом поспешу поделиться.
Есть следующий код:
...
int main()
{
    ...
    for (int i = 0; i < 10; i++) {
        // Что-то делаю с участием переменной i
    }
    ...
}

Как известно, в языке С++ можно определять переменную прямо в цикле for. В приведенном примере это переменная i. Вопрос в следующем: какова область видимости внутренней переменной i? Многие (и я, до не давнего времени) сказали бы: "Ну, это просто: область видимости i расположена внутри скобок цикла for. То есть за пределами цикла внутренняя переменная исчезает". И они были бы совершенно правы, но лишь в том случае, если подразумевается использование современных компиляторов, поддерживающих стандарт языка. А если компилятор старый? Я, например, вынужден пользоваться на работе именно таким. Так вот, в старых компиляторах область видимости внутренней переменной i совпадает с областью видимости самого цикла for! То есть, в вышеприведенном примере, i будет существовать до конца функции main()! А это плохо, так как мы не подразумевали, что i окажется где-либо вне цикла. Что будет, если мы напишем так:
...
int main()
{
    ...
    for (int i = 0; i < 10; i++) {
        // Что-то делаю с участием переменной i
    }
    ...
    int i; // новая переменная, нужная для каких-то других целей
    ...
}

Но так нельзя! Будет нарушено правило одного определения, так как i, определенная внутри цикла for, будет конфликтовать с новой i.
Есть очень простое решение указанной проблемы, одинаково действенное для любого компилятора: заключите цикл в дополнительные скобки!
...
int main()
{
    ...
    { for (int i = 0; i < 10; i++) {
        // Что-то делаю с участием переменной i
    } }
    ...
    int i;  // теперь i из цикла не имеет к 
            // этой i никакого отношения
}

Создав дополнительными фигурными скобками маленькую область видимости, мы гарантировали себя от ошибок, рассмотренных выше.

[Обновления: Вск, 10 Июнь 2007 23:58]

Известить модератора

Re: Заметки программиста С++ [сообщение #493 является ответом на сообщение #443] Пнд, 11 Июнь 2007 23:20 Переход к предыдущему сообщения
Денис Шевченко в настоящее время не в онлайне  Денис Шевченко
Сообщений: 53
Зарегистрирован: Март 2005
Географическое положение: Москва
Member
Еще один прикол - триграфы в строчных комментариях.
Есть следующий код:
...
int main()
{
    ...
    if ( i < 3 ) {
        i = 3;         //????????????/
        int b = 10;    // А это очень важное действие!
    }
}

Так вот, очень важное действие с переменной b НЕ произойдет! Почему, спросите вы? Обратите внимание на три последних знака в первом комментарии, в котором нам почему-то понадобилось указать много знаков вопроса: "??/" Эти три знака образуют триграф, который преобразуется к знаку "\", соединяющий строки (конкатенация)! Таким образом, комментарий с вопросительными знаками будет объединен со строкой, в которой должно произойти очень важное действие. И важная строка будет также закомментированной. Я проверял, это действительно так!
Причем ваш редактор, скорее всего, показывающий закомментированный текст определенным цветом, в данной ситуации НЕ покажет строку с очень важным действием как закомментаренную. И компилятор не ругнется. Впрочем, это относится не ко всем компиляторам, но g++ точно не ругнется, ибо он не просматривает содержимое комментариев, считая это бесполезным. Но в приведенном мною примере такой подход приведет к ошибке.
Совет один: не допускайте триграфов в комментариях.

[Обновления: Пнд, 11 Июнь 2007 23:26]

Известить модератора

Предыдущая тема: Графика или информация?
Следующая тема: Русский сайт, посвященный С++
Переход к форуму: