Блог ← GOTO. Благословение или проклятие?

C, goto

Ах, какой ужас. Вы используете goto? Да что вы, это же возвращение в дебри «васика» и «понятного» кода. Где структурное программирование? Где выполнение заповедей Дейкстры? Ай-яй-яй.

Некоторые мои знакомые с пеной у рта доказывают, что использовать goto в языках высокого уровня — это зло, больше которого, наверное, нет в программировании. Если ты используешь goto — ты не умеешь программировать и твоим программам не хватает логики (ага :).

В общем случае использование goto не оправдано и действительно является источником больших проблем. Однако в каждом правиле есть исключения, только подчеркивающие его. Поэтому далее следует «высший пилотаж».

Хороший пример использования goto

for(x = 0; x < 100; x++)
   for(y = 0; y < 100; y++)
      for(z = 0; z < 100; z++)
         if(!Matrix[x][y][z])
            goto COLLISION_EXIT;
         else          
            Matrix[x][y][z]++;
  
COLLISION_EXIT:

Сравним с

for(x = 0, Flag = FALSE; x < 100 && (!Flag); x++)
   for(y = 0; y < 100 && (!Flag); y++)
      for(z = 0; z < 100 && (!Flag); z++)
         if(!Matrix[x][y][z])
            Flag = TRUE;
         else          
            Matrix[x][y][z]++;

«Преимущества» второго примера очевидны:


  • «Понятный» код
  • «Быстрее»
  • «Никаких» лишних телодвижений при возникновении исключительной ситуации

Еще один частый пример правильного использования goto — это обработка ошибок.

int
foo(...)
{
    int error;

    if (какая-то ошибка номер 1) {
        error = EFOO;
        goto out;
    }

    if (какая-то ошибка номер 2) {
        error = EBAR;
        goto out;
    }

    /* Кусок кода выполняющийся когда нет никаких ошибок */
        error = 0;

    out:
    /* Освобождение ресурсов, сброс флагов и т.п. */

    return error;
}

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

Теперь если кто-то вам скажет «никогда не используйте goto» — можно смело ответить «никогда не говори никогда».

Ссылки:
http://julipedia.blogspot.com/2005/08/using-gotos-in-c.html
http://www.gmonline.demon.co.uk/cscene/CS7/CS7-02.html

30 Мая, 2008
« Конвертирование (La)TeX в DOCГенерация пароля «unix-way» »

3 комментария

  1. Вадим Вадим:

    Нужно понимать, что правило "не использовать goto" является не фанатическим преклонением перед Дейкстрой, а неким соглашением между программистами, которые пишут и поддерживают один и тот же код. Трудно предвидеть к чему приведет добавление новой функциональности в код, содержащий goto, как и, например, с функциями имеющими несколько точек возврата (return). На языке антипаттернов это называется Spaghetti code (http://ru.wikipedia.org/wiki/Спагетти-код).
    Теперь попытаемся разобрать примеры и найти альтернативы:

    for(x = 0; x < 100; x++)
    for(y = 0; y < 100; y++)
    for(z = 0; z < 100; z++)
    if(!Matrix[x][y][z])
    goto COLLISION_EXIT;
    else
    Matrix[x][y][z]++;

    COLLISION_EXIT:

    Довольно компактно и понятно на первый взгляд. Логично, что после метки COLLISION_EXIT: содержится какой-нибудь обрабатывающий код. Но вот если все циклы дойдут до конца без ошибки (все элементы массива равны 1 например), то нам прийдется как-то пропустить обработку ошибки. Хм ...незадача. Очевидно нам поможет еще одно условие после цикла и еще один оператор goto и еще одна метка. Например:

    for(x = 0; x < 100; x++)
    for(y = 0; y < 100; y++)
    for(z = 0; z < 100; z++)
    if(!Matrix[x][y][z])
    goto COLLISION_EXIT;
    else
    Matrix[x][y][z]++;

    if ( 100 ==x && 100 == y && 100 == z )
    {
    goto CONTINUE:
    }

    COLLISION_EXIT:
    //handle error
    CONTINUE:
    //continue

    Хм...а если в случае ошибки и перехода на COLLISION_EXIT: нам нет необходимости выполнять дальнейший код? Правильно - можно вставить еще один goto и еще одну метку...и.т.д, после этого можно смело отдавать код товарищу - пусть разбирается и громко матерится.

    Второй пример является экономией на вызове обрабатывающей функции (действительно можно сэкономить пару байт ;) ). Я имею ввиду вот такой код:

    int
    out(int error, ...)
    {
    //....
    }

    int
    foo(...)
    {
    int error;

    if (какая-то ошибка номер 1) {
    error = EFOO;
    out(error, ...);
    } else

    if (какая-то ошибка номер 2) {
    error = EBAR;
    out(error, ...);
    } else
    {
    /* Кусок кода выполняющийся когда нет никаких ошибок */
    error = 0;

    out(error, ...)
    }
    return error;
    }

    Вывод: Использовать или не использовать goto - ваше личное дело, когда вы
    1) Разрабатываете небольшую, немасштабируемую или неподдерживаемую систему.
    2) У вас есть четкая договоренность об использовании goto с другими участниками разработки (единый стиль)
    3) В маленьких неизменяемых локальных блоках, если это кажется наилучшим выходом из ситуации. (как, например, если бы пример 1 был полным телом локальной функции).

    01 Июн, 2008
  2. Вадим Вадим:

    Вдогонку мысли о goto очень авторитетного С++ программиста ;).
    http://insidecpp.ru/art/7/

    01 Июн, 2008
  3. vti (Автор) vti (Автор):

    В примерах лишь куски кода, а не рабочая реализация. К тому же как перепрыгивать на альтернативный «рабочий код» показано во втором примере.

    01 Июн, 2008

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

OpenID Войти с помощью OpenID
[u] [i] [url] [img] [code] [list]...
Не будет опубликован
 
2008 GPL
все тэги »

Подписка

rss Записи
rss Комментарии

Недавние записи

архив »

Недавние комментарии

Страница сгенерирована за 0.03396с
Крутится на Catalyst