Ах, какой ужас. Вы используете 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
Войти с помощью OpenID
Нужно понимать, что правило "не использовать 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 был полным телом локальной функции).
Вдогонку мысли о goto очень авторитетного С++ программиста ;).
http://insidecpp.ru/art/7/
В примерах лишь куски кода, а не рабочая реализация. К тому же как перепрыгивать на альтернативный «рабочий код» показано во втором примере.