Правила программирования на Си и Си++

Немедленно обрабатывайте особые случаи


Пишите свои алгоритмы таким образом, чтобы они обрабатывали вырожденные случаи немедленно. Вот тривиальный пример того, как сделать это неправильно:

print( const char *str )

{

    if( !*str )        // ничего не делать, строка пуста

       return;

    while( *str )

       putchar( *str++ );

}

Оператор if тут не нужен, потому что этот случай хорошо обрабатывается циклом while.

Листинги 2 и 3 демонстрируют более реалистический сценарий. Листинг 2 определяет умышленно наивный заголовок связанного списка и функцию для удаления из него элемента.

Листинг 2. Связанный список: вариант 1

typedef struct

node



{

    struct node *next, *prev;

    //...

} node;

/p>

node *head;

remove( node **headp, node *remove )

{

    // Удалить элемент, на который указывает remove, из

    // списка, на начало которого указывает *headp.

    if( *headp == remove )  // Этот элемент в начале списка.

    {

       if( remove-next )        // Если это не единственный

          remove-next-prev = NULL; // элемент в списке, то

                                     // поместите следующий

                                     // за ним элемент первым

                                     // в списке.

       *headp = remove-next;

    }

    else               // Элемент находится в середине списка

    {

       remove-prev-next = remove-next;

       if( remove-next )

          remove-next-prev = remove-prev;

    }

}

Листинг 3 делает то же самое, но я модифицировал указатель на предыдущий элемент в структуре node, поместив туда адрес поля next

предыдущего элемента вместо указателя на всю структуру. Это простое изменение означает, что первый элемент больше не является особым случаем, поэтому функция remove становится заметно проще.

Смысл этого состоит в том, что незначительная переделка этой задачи позволяет мне использовать алгоритм, не имеющий особых случаев, этим самым упрощая программу. Конечно, эта простота не дается бесплатно —

теперь стало невозможным перемещение по списку в обратном направлении — но нам ведь это может быть и не нужно.

Листинг 3. Связанный список: вариант 2

typedef struct

node

{

    struct node *next, **prev; // == К prev добавлен символ *

    //

} node;

/p>

node *head;

/p>

remove( node **headp, node *remove )

{

    if( *(remove-prev) = remove-next ) // если не в конце

      remove-next-prev = remove-prev; // списка, то уточнить

                                         // следующий элемент

}



Содержание раздела