В этой теме мы рассмотрим примеры правильного использования goto
C или C ++. Он вдохновлен ответом, за который люди проголосовали, потому что думали, что я шучу.
Резюме (ярлык изменен с оригинала, чтобы прояснить намерения):
infinite_loop:
// code goes here
goto infinite_loop;
Почему лучше альтернатив:
- Это специфично.
goto
это языковая конструкция, которая вызывает безусловный переход. Альтернативы зависят от использования структур, поддерживающих условные переходы, с вырожденным условием Always-True. - Этикетка документирует намерение без дополнительных комментариев.
- Читателю не нужно сканировать промежуточный код на ранних этапах
break
(хотя для беспринципного хакера все еще возможно смоделироватьcontinue
с раннимgoto
).
Правила:
- Представьте, что готофобы не победили. Понятно, что приведенное выше нельзя использовать в реальном коде, потому что это противоречит устоявшейся идиоме.
- Предположим, что все мы слышали о том, что Goto считается вредным, и знаем, что goto можно использовать для написания кода спагетти.
- Если вы не согласны с примером, критикуйте его только по техническим причинам («Потому что людям не нравится goto» - это не техническая причина).
Посмотрим, сможем ли мы говорить об этом, как взрослые.
редактировать
Этот вопрос кажется законченным. Это дало несколько качественных ответов. Спасибо всем, особенно тем, кто серьезно отнесся к моему примеру с петлей. Большинство скептиков было обеспокоено отсутствием возможности блокировки. Как отметил @quinmars в комментарии, вы всегда можете поместить фигурные скобки вокруг тела цикла. Я мимоходом отмечу это for(;;)
и while(true)
не даю вам брекеты бесплатно (их пропуск может вызвать досадные ошибки). Во всяком случае, я не буду тратить больше ваш мозг власти на этой мелочи - я могу жить с безвредным и идиоматическим for(;;)
и while(true)
(точно так же , если я хочу , чтобы сохранить свою работу).
Принимая во внимание другие ответы, я вижу, что многие люди считают goto
что-то, что вам всегда нужно переписывать по-другому. Конечно, вы можете избежать a goto
, введя цикл, дополнительный флаг, стек вложенных if
s или что-то еще, но почему бы не подумать, goto
может ли это лучший инструмент для работы? Другими словами, на сколько уродства готовы вынести люди, чтобы избежать использования встроенной языковой функции по прямому назначению? Я считаю, что даже добавление флага - это слишком высокая цена. Мне нравится, что мои переменные представляют объекты в области проблем или решений. «Исключительно для того, чтобы избежать» - goto
это не значит.
Я приму первый ответ, который дал шаблон C для перехода к блоку очистки. ИМО, это самый веский аргумент в пользу goto
из всех опубликованных ответов, конечно, если вы измеряете это искажениями, которые ненавистник должен пройти, чтобы избежать этого.
for (;;)
) не имеет неожиданных точек входа.Ответы:
Вот одна уловка, которую я слышал от людей. Однако я никогда не видел его в дикой природе. И это применимо только к C, потому что в C ++ есть RAII, чтобы сделать это более идиоматично.
void foo() { if (!doA()) goto exit; if (!doB()) goto cleanupA; if (!doC()) goto cleanupB; /* everything has succeeded */ return; cleanupB: undoB(); cleanupA: undoA(); exit: return; }
источник
Классическая потребность в GOTO в C следующая
for ... for ... if(breakout_condition) goto final; final:
Нет простого способа выйти из вложенных циклов без перехода.
источник
Вот мой нелепый пример (от Stevens APITUE) для системных вызовов Unix, которые могут быть прерваны сигналом.
restart: if (system_call() == -1) { if (errno == EINTR) goto restart; // handle real errors }
Альтернатива - вырожденный цикл. Эта версия читается как английский «если системный вызов был прерван сигналом, перезапустите его».
источник
continue
это простоgoto
маска.else break
s (по одному на каждое if), чтобы сделать его эквивалентным ... что я могу сказать ...goto
или нет, ваш код настолько хорош, как вы :(Если устройству Даффа не требуется goto, то и вам не нужно! ;)
void dsend(int count) { int n; if (!count) return; n = (count + 7) / 8; switch (count % 8) { case 0: do { puts("case 0"); case 7: puts("case 7"); case 6: puts("case 6"); case 5: puts("case 5"); case 4: puts("case 4"); case 3: puts("case 3"); case 2: puts("case 2"); case 1: puts("case 1"); } while (--n > 0); } }
Код выше из Википедии записи .
источник
Кнут написал статью «Структурированное программирование с операторами GOTO», вы можете получить ее, например, здесь . Вы найдете там много примеров.
источник
Очень распространен.
do_stuff(thingy) { lock(thingy); foo; if (foo failed) { status = -EFOO; goto OUT; } bar; if (bar failed) { status = -EBAR; goto OUT; } do_stuff_to(thingy); OUT: unlock(thingy); return status; }
Единственный случай, который я когда-либо использовал,
goto
- это прыгать вперед, обычно из блоков, а не в блоки. Это позволяет избежать злоупотребленийdo{}while(0)
и других конструкций, которые увеличивают вложенность, сохраняя при этом читаемый структурированный код.источник
do_stuff(thingy) { RAIILockerObject(thingy); ..... /* -->*/ } /* <---*/
:)try { lock();..code.. } finally { unlock(); }
но да, у C нет эквивалента.Я ничего не имею против gotos в целом, но я могу придумать несколько причин, по которым вы не захотите использовать их для цикла, как вы упомянули:
источник
Одно хорошее место для использования goto - это процедура, которая может прерваться в нескольких точках, каждая из которых требует различных уровней очистки. Gotophobes всегда может заменить gotos структурированным кодом и серией тестов, но я думаю, что это более просто, потому что он устраняет чрезмерные отступы:
источник
freeResourcesCloseFileQuit
,closeFileQuit
иquit
.@ fizzer.myopenid.com: размещенный вами фрагмент кода эквивалентен следующему:
while (system_call() == -1) { if (errno != EINTR) { // handle real errors break; } }
Я определенно предпочитаю эту форму.
источник
Несмотря на то, что со временем я возненавидел этот шаблон, он встроен в программирование COM.
#define IfFailGo(x) {hr = (x); if (FAILED(hr)) goto Error} ... HRESULT SomeMethod(IFoo* pFoo) { HRESULT hr = S_OK; IfFailGo( pFoo->PerformAction() ); IfFailGo( pFoo->SomeOtherAction() ); Error: return hr; }
источник
Вот пример хорошего goto:
// No Code
источник
Я видел, как goto используется правильно, но ситуации обычно уродливые. Это только тогда, когда использование самого
goto
себя намного меньше, чем у оригинала. @Johnathon Holland проблема в том, что ваша версия менее ясна. люди, кажется, боятся локальных переменных:void foo() { bool doAsuccess = doA(); bool doBsuccess = doAsuccess && doB(); bool doCsuccess = doBsuccess && doC(); if (!doCsuccess) { if (doBsuccess) undoB(); if (doAsuccess) undoA(); } }
И я предпочитаю такие петли, но некоторые предпочитают
while(true)
.for (;;) { //code goes here }
источник
== true
снята. В любом случае это ближе к моему настоящему стилю. :)Меня беспокоит то, что вы теряете область видимости блока; любые локальные переменные, объявленные между gotos, остаются в силе, если цикл когда-либо прерывается. (Возможно, вы предполагаете, что цикл выполняется вечно; однако я не думаю, что это то, что задавал исходный автор вопроса.)
Проблема области видимости больше связана с C ++, поскольку некоторые объекты могут зависеть от того, как их dtor вызывается в соответствующее время.
Для меня лучшая причина использовать goto - это во время многоэтапного процесса инициализации, когда жизненно важно, чтобы все inits были отменены в случае сбоя одного из них, а-ля:
if(!foo_init()) goto bye; if(!bar_init()) goto foo_bye; if(!xyzzy_init()) goto bar_bye; return TRUE; bar_bye: bar_terminate(); foo_bye: foo_terminate(); bye: return FALSE;
источник
Я сам не использую goto, однако однажды я работал с человеком, который использовал бы их в определенных случаях. Если я правильно помню, его обоснование было связано с проблемами производительности - у него также были определенные правила, как это сделать . Всегда в одной и той же функции, а метка всегда была НИЖЕ оператора goto.
источник
#include <stdio.h> #include <string.h> int main() { char name[64]; char url[80]; /*The final url name with http://www..com*/ char *pName; int x; pName = name; INPUT: printf("\nWrite the name of a web page (Without www, http, .com) "); gets(name); for(x=0;x<=(strlen(name));x++) if(*(pName+0) == '\0' || *(pName+x) == ' ') { printf("Name blank or with spaces!"); getch(); system("cls"); goto INPUT; } strcpy(url,"http://www."); strcat(url,name); strcat(url,".com"); printf("%s",url); return(0); }
источник
@ Грег:
Почему бы не сделать ваш пример таким:
void foo() { if (doA()) { if (doB()) { if (!doC()) { UndoA(); UndoB(); } } else { UndoA(); } } return; }
источник