GOTO все еще считается вредным? [закрыто]

282

Все знают о письмах Дейкстры редактору: переходите к утверждению, которое считается вредным (также здесь .html transcripts и здесь .pdf), и с того времени был огромный толчок, чтобы по возможности отказаться от оператора goto. Хотя можно использовать goto для создания неуправляемого, растягивающегося кода, он, тем не менее, остается в современных языках программирования . Даже усовершенствованная структура управления продолжением в Схеме может быть описана как сложный переход.

Какие обстоятельства оправдывают использование goto? Когда лучше избегать?

В качестве дополнительного вопроса: C предоставляет пару функций setjmp и longjmp, которые обеспечивают возможность перехода не только в текущем кадре стека, но и в любом из вызывающих кадров. Должны ли они считаться такими же опасными, как goto? Более опасно?


Сам Дейкстра пожалел о том звании, за которое не нес ответственности. В конце EWD1308 (также здесь .pdf) он написал:

Наконец короткая история для записи. В 1968 году в сообщениях АСМ был опубликован мой текст под заголовком « Заявление Гото считалось вредным », на который в последующие годы чаще всего будут ссылаться, к сожалению, однако, часто авторы, которые видели его не больше, чем его title, который стал краеугольным камнем моей славы, став шаблоном: мы увидели бы все виды статей под заголовком «X считается вредным» практически для любого X, включая статью под названием «Дейкстра считается вредным». Но что случилось? Я представил документ под заголовком « Дело против заявления Гото»", который, чтобы ускорить его публикацию, редактор превратился в" письмо в редакцию ", и в процессе он дал ему новое название своего собственного изобретения! Редактором был Никлаус Вирт.

Хорошо продуманная классическая статья на эту тему, которую можно сравнить с темой Дейкстры, - « Структурное программирование с переходом к утверждениям» , написанная Дональдом Кнутом. Чтение обоих помогает восстановить контекст и недогматическое понимание предмета. В этой статье сообщается мнение Дейкстры по этому делу, и оно еще более убедительно:

Дональд Э. Кнут: Я полагаю, что, представляя такую ​​точку зрения, я на самом деле не сильно не согласен с идеями Дейкстры, поскольку он недавно написал следующее: «Пожалуйста, не попадитесь в ловушку веры в то, что я ужасно догматичен перейти к утверждению]. У меня неприятное ощущение, что другие делают из этого религию, как будто концептуальные проблемы программирования могут быть решены одним трюком, простой формой дисциплины кодирования! "

MaD70
источник
1
Goto в C # отличается от goto, о котором говорил Дейкстра, именно по тем причинам, о которых говорил Дейкстра. Это goto как вредно, как это было тогда, но и намного менее необходимо, потому что современные языки предоставляют альтернативные структуры управления. C # goto чрезвычайно ограничен.
Джалф
25
Gotos хороши, когда они добавляют ясности. Если у вас длинный вложенный цикл, может быть лучше выйти из него, чем устанавливать переменные «break» и прерывать до тех пор, пока вы не выйдете.
Сименджо
28
Если у вас есть вложенный цикл на 4 глубины (не то, чтобы это было хорошо), для разрыва всего необходимо установить временные значения. Goto здесь гораздо понятнее, и IDE должна легко показать, где goto. Тем не менее, использование goto должно быть редким, и, по моему мнению, нужно переходить к следующему шагу, чтобы пропустить код
simendsjo
7
Я предлагаю вам прочитать девять тысяч и одну тему с тегами goto.
Матти Вирккунен
5
Есть одно явное хуже, чем использование goto: совместное использование инструментов структурированного программирования для реализации goto.

Ответы:

184

Следующие утверждения являются обобщениями; Хотя всегда можно заявить об исключении, обычно (по моему опыту и скромному мнению) риски не стоят.

  1. Неограниченное использование адресов памяти (как GOTO, так и необработанных указателей) предоставляет слишком много возможностей совершать ошибки, которых легко избежать.
  2. Чем больше способов достичь определенного «места» в коде, тем менее уверенным может быть состояние системы на данный момент. (Увидеть ниже.)
  3. Структурное программирование ИМХО - это не «избегание GOTO», а больше о том, чтобы структура кода соответствовала структуре данных. Например, повторяющаяся структура данных (например, массив, последовательный файл и т. Д.) Естественным образом обрабатывается повторяющейся единицей кода. Наличие встроенных структур (например, while, for, while, for-each и т. Д.) Позволяет программисту избежать утомительного повторения одних и тех же шаблонов кодированного клише.
  4. Даже если GOTO - это детали реализации низкого уровня (не всегда!), Она ниже уровня, о котором должен думать программист. Сколько программистов балансируют свои личные чековые книжки в двоичном формате? Сколько программистов беспокоится о том, какой сектор на диске содержит конкретную запись, вместо того, чтобы просто предоставлять ключ для механизма базы данных (и сколько способов может пойти не так, если мы действительно напишем все наши программы в терминах секторов физического диска)?

Сноски к вышесказанному:

Что касается пункта 2, рассмотрим следующий код:

a = b + 1
/* do something with a */

В точке «сделать что-то» в коде мы можем с высокой степенью достоверности заявить, что aбольше, чем b. (Да, я игнорирую возможность незаполненного целочисленного переполнения. Давайте не будем искать простой пример.)

С другой стороны, если код читался так:

...
goto 10
...
a = b + 1
10: /* do something with a */
...
goto 10
...

Множество способов добраться до метки 10 означает, что нам нужно работать гораздо усерднее, чтобы быть уверенными в отношениях между aи bна этом этапе. (На самом деле, в общем случае это неразрешимо!)

Что касается пункта 4, само понятие «идти куда-то» в коде - всего лишь метафора. Ничто на самом деле не «движется» нигде внутри процессора, кроме электронов и фотонов (для ненужного тепла). Иногда мы отказываемся от метафоры для другой, более полезной. Я помню, что встречал (несколько десятилетий назад!) Язык, на котором

if (some condition) {
  action-1
} else {
  action-2
}

был реализован на виртуальной машине путем компиляции action-1 и action-2 как внеплановых подпрограмм без параметров, а затем с использованием одного кода операции VM с двумя аргументами, который использовал логическое значение условия для вызова одного или другого. Концепция была просто «выбрать, что вызвать сейчас», а не «иди сюда или иди туда». Опять просто смена метафоры.

joel.neely
источник
2
Хороший вопрос. В языках более высокого уровня goto на самом деле ничего не значит (рассмотрите возможность перехода между методами в Java). Функция Haskell может состоять из одного выражения; попробуйте выскочить из этого с goto!
Механическая улитка
2
Постскриптум работает как ваш пункт 4 примера.
Люсер Дрог
Smalltalk работает аналогично примеру 4, если под «аналогично» вы подразумеваете «ничего подобного процедурным языкам». : P Нет управления потоком в языке; все решения обрабатываются с помощью полиморфизма ( trueи falseбывают разных типов), и каждая ветвь if / else является в основном лямбда-выражением.
Чао
Это действительные пункты, но в конце они просто повторяют, насколько плохим может быть goto, «если его неправильно использовать». Разрыв, продолжение, выход, возврат, gosub, settimeout, global, include и т. Д. - все это современные методы, которые требуют умственного отслеживания потока вещей и могут быть использованы не по назначению для создания спагетти-кода и пропуска для создания неопределенности переменных состояний. Чтобы быть справедливым, хотя я никогда не видел плохого использования goto из первых рук, я также только когда-либо видел, что он использовался один или два раза. Это говорит о том, что всегда есть вещи получше.
Beejor
gotoна современном языке программирования (Go) stackoverflow.com/a/11065563/3309046 .
nishanths
245

GKO комикс XKCD

Мой коллега сказал, что единственная причина использовать GOTO - это если вы запрограммировали себя так далеко, что это единственный выход. Другими словами, правильный дизайн заблаговременно, и вам не нужно будет использовать GOTO позже.

Я подумал, что этот комикс прекрасно иллюстрирует: «Я мог бы реструктурировать поток программы или использовать вместо этого один маленький« GOTO »». GOTO - слабый выход, когда у вас слабый дизайн. Велоцирапторы охотятся на слабых .

Jim McKeeth
источник
41
GOTO может совершить «прыжок» из одного произвольного места в другое. Велоцираптор прыгнул сюда из ниоткуда!
rpattabi
29
я вообще не нахожу шутку смешной, потому что все знают, что вам также нужно связаться, прежде чем вы сможете выполнить.
Кинджал Диксит
31
Ваш коллега не прав, и, очевидно, не читал статью Кнута.
Джим Балтер
27
Я не видел конца запутанного и искаженного кода на протяжении многих лет, чтобы избежать перехода. @jimmcKeeth, вышеприведенный мультфильм xkcd не устанавливает, что goto является слабым. Это высмеивает истерию вокруг его использования.
4
Вы понимаете, что исходный код библиотеки Delphi содержит операторы goto. И это подходящее использование goto.
Дэвид Хеффернан
131

Иногда допустимо использовать GOTO в качестве альтернативы обработке исключений внутри одной функции:

if (f() == false) goto err_cleanup;
if (g() == false) goto err_cleanup;
if (h() == false) goto err_cleanup;

return;

err_cleanup:
...

Код COM, кажется, попадает в этот шаблон довольно часто.

Роб Уокер
источник
29
Я согласен, есть законные случаи использования, когда goto может упростить код и сделать его более читабельным / обслуживаемым, но, похоже, что-то вроде готофобии, плавающей вокруг ...
Pop Catalin
48
@Bob: Трудно переместить код err_cleanup в подпрограмму, если он очищает локальные переменные.
Ники,
4
На самом деле, я использовал его в COM / VB6 только потому, что у меня не было альтернативы, а не потому, что это была альтернатива. Как я счастлив в настоящее время с try / catch / finally.
Руи Кравейро
10
@ user4891 Идиоматический способ C ++ - это не try {} catch () {cleanup; }, а скорее RAII, где ресурсы, которые должны быть очищены, делаются так в деструкторах. Каждый конструктор / деструктор управляет ровно одним ресурсом.
Дэвид Стоун
5
Есть два способа написания этого на C без goto; и оба намного короче. Либо: if (f ()) if (g ()) if (h ()) вернуть успех; очистка (); возврат неудачи; или: if (f () && g () && h ()) вернет успех; очистка (); возврат неудачи;
LHP
124

Я могу вспомнить только один раз, используя goto. У меня была серия из пяти вложенных подсчитанных циклов, и мне нужно было иметь возможность вырваться из всей структуры изнутри на основе определенных условий:

for{
  for{
    for{
      for{
        for{
          if(stuff){
            GOTO ENDOFLOOPS;
          }
        }
      }
    }
  }
}

ENDOFLOOPS:

Я мог бы просто объявить булеву переменную break и использовать ее как часть условия для каждого цикла, но в этом случае я решил, что GOTO так же практичен и удобен для чтения.

Никакие велоцирапторы не напали на меня.

shsteimer
источник
83
«Преобразуйте его в функцию и замените goto на return :)», а разница есть? правда какая разница? не вернуть идти также? Возвращает также тормозит структурированный поток, как goto, и в этом случае они делают это одинаково (даже если goto можно использовать для более подлых вещей)
Pop Catalin
38
Вложение большого количества циклов обычно является запахом кода. Если вы не делаете, например, 5-мерное умножение массива, трудно представить ситуацию, когда некоторые внутренние циклы не могли бы быть с пользой извлечены в меньшие функции. Как и все эмпирические правила, я полагаю, есть несколько исключений.
Даг МакКлин
5
Замена на возврат работает только в том случае, если вы используете язык, поддерживающий возврат.
Лорен Печтел
31
@leppie: поколение, которое восстало против gotoи дало нам структурированное программирование, также отказалось от ранней отдачи по той же причине. Все сводится к тому, насколько читаем код, насколько четко он выражает намерение программиста. Создание функции только для того, чтобы избежать использования некорректного ключевого слова, приводит к плохой сплоченности: лекарство хуже болезни.
Грязь
21
@ButtleButkus: Честно говоря, это так же плохо, если не хуже. По крайней мере, с goto, можно явно указать цель. С помощью break 5;(1) мне нужно сосчитать замыкания цикла, чтобы найти пункт назначения; и (2) если структура цикла когда-либо изменяется, вполне может потребоваться изменить это число, чтобы сохранить правильность назначения. Если я собираюсь избегать goto, то выигрыш должен заключаться в том, что нет необходимости вручную отслеживать подобные вещи.
cHao
93

У нас уже было это обсуждение, и я поддерживаю свою точку зрения .

Кроме того, я сыт по горло людьми, которые описывают языковые структуры более высокого уровня как « gotoзамаскированные», потому что они явно не понимают суть вообще . Например:

Даже усовершенствованная структура управления продолжением в Схеме может быть описана как сложный переход.

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

Точно так же высказывание «GOTO - это инструмент, и, как и все инструменты, им можно пользоваться и злоупотреблять», совершенно не соответствует действительности. Ни один современный строитель не будет использовать камень и утверждать, что он «инструмент». Скалы были заменены молотками. gotoбыл заменен контрольными структурами. Если бы строитель оказался в дикой природе без молотка, он, конечно, вместо этого использовал бы камень. Если программист должен использовать более низкий язык программирования, который не имеет функции X, ну, конечно, она может использовать gotoвместо этого. Но если она использует его где-то еще вместо соответствующей языковой функции, она явно не поняла язык должным образом и использует его неправильно. Это действительно так просто.

Конрад Рудольф
источник
82
Конечно, правильное использование камня не как молоток. Одним из его правильных применений является шлифовальный камень или для заточки других инструментов. Даже скромный камень при правильном использовании - хороший инструмент. Вы просто должны найти правильное использование. То же самое касается Гото.
Кибби
10
Итак, как правильно использовать Goto? Для каждого мыслимого случая есть другой инструмент, более подходящий. И даже ваш точильный камень в настоящее время заменен высокотехнологичными инструментами, даже если они все еще сделаны из камня. Существует большая разница между сырьем и инструментом.
Конрад Рудольф
8
@jalf: Goto, безусловно , делает существует в C #. См. Stackoverflow.com/questions/359436/…
Джон Скит
51
Я встревожен, так много людей одобряют этот пост. Ваш пост показался вам эффективным только потому, что вы никогда не задавались вопросом, какую логику вы фактически выполняете, поэтому вы не заметили свою ошибку. Позвольте мне перефразировать весь ваш пост: «Существует превосходный инструмент для goto в любой ситуации, поэтому gotos никогда не следует использовать». Это логическое двухусловное условие, и поэтому весь ваш пост, по сути, задает вопрос: «Откуда вы знаете, что в любой ситуации есть превосходный инструмент для goto?»
Кодирование со стилем
10
@ Кодирование: Нет, вы полностью пропустили суть публикации. Это был ответный удар, а не изолированный, полный аргумент. Я просто указал на ошибку в главном аргументе «за» goto. Вы правы, поскольку я не предлагаю аргумент против gotoper se - я не собирался - поэтому нет никаких вопросов.
Конрад Рудольф
91

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

Goto может быть хорош для конечных автоматов. Оператор switch в цикле (в порядке типичной важности): (а) фактически не является представителем потока управления, (б) некрасиво, (в) потенциально неэффективен в зависимости от языка и компилятора. Таким образом, вы заканчиваете тем, что пишете одну функцию на состояние и выполняете такие действия, как «return NEXT_STATE;» который даже похож на гото.

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

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

setjmp / longjmp может быть полезен для реализации исключений или поведения, подобного исключению. Несмотря на то, что они не одобряются повсеместно, исключения обычно считаются «действительной» структурой контроля

setjmp / longjmp «более опасны», чем goto, в том смысле, что их сложнее правильно использовать, не говоря уже о понятном.

Никогда не было и не будет никакого языка, на котором было бы наименее сложно написать плохой код. Дональд Кнут.

Вывод goto из C не облегчит написание хорошего кода на C. На самом деле, он скорее упустит тот момент, что C должен выступать в качестве прославленного языка ассемблера.

Далее это будут «указатели, считающиеся вредными», затем «печать утки, считающаяся вредной». Тогда кто будет защищать вас, когда они заберут вашу небезопасную программную конструкцию? А?

Стив Джессоп
источник
14
Лично, это комментарий, который я бы дал чек. Одна вещь, на которую я хотел бы обратить внимание читателей, состоит в том, что эзотерический термин «конечные автоматы» включает такие повседневные вещи, как лексические анализаторы. Проверьте вывод lex somtime. Полный gotos.
TED
2
Вы можете использовать оператор switch внутри цикла (или обработчик событий), чтобы сделать конечные автоматы просто отлично. Я сделал много конечных автоматов без необходимости использовать jmp или goto.
Скотт Уитлок
11
+1 Эти стрелки на конечных автоматах отображаются на «goto» более близко, чем на любую другую структуру управления. Конечно, вы можете использовать переключатель внутри цикла - точно так же, как вы можете использовать кучу gotos вместо времени для других проблем, но обычно это идея; в этом весь смысл этой дискуссии.
Эдмунд,
2
Могу я процитировать вас в этом последнем абзаце?
Крис Латс
2
И здесь, в 2013 году, мы уже достигли (и отчасти прошли мимо) фазы «указатели считаются вредными».
Исия Медоуз
68

В Linux: Использование goto В Kernel Code на Kernel Trap обсуждается с Линусом Торвальдсом и «новым парнем» использование GOTO в коде Linux. Там есть несколько очень хороших моментов, и Линус одет в это обычное высокомерие :)

Некоторые отрывки:

Линус: «Нет, вам промыли мозги люди из CS, которые думали, что Никлаус Вирт действительно знал, о чем говорит. Он не знал. У него нет чертова подсказка».

-

Линус: «Я думаю, что с goto все в порядке, и они часто более читабельны, чем большие отступы».

-

Линус: «Конечно, в глупых языках, таких как Паскаль, где ярлыки не могут быть описательными, goto могут быть плохими».

Marcio Aguiar
источник
9
Это хороший момент, как? Они обсуждают его использование на языке, который не имеет ничего другого. Когда вы программируете на ассемблере, все переходы и переходы выполняются . А C есть и был «переносимым языком ассемблера». Более того, цитаты, которые вы цитируете, ничего не говорят о том, почему он думает, что goto - это хорошо.
Джалф
5
Вот это да. Это разочаровывает, чтобы читать. Вы могли бы подумать, что такой хороший парень из ОС, как Линус Торвальдс, будет знать лучше, чем сказать это. Pascal (старый школьный паскаль, а не современная версия Object) был тем, чем Mac OS Classic был написан в течение периода 68 тысяч лет, и он был самой передовой операционной системой своего времени.
Мейсон Уилер
6
@mason Classic Mac OS имела несколько библиотек Pascal (в конце концов, среда выполнения Pascal занимала слишком много памяти в ранних версиях Mac), но основная часть кода была написана на ассемблере, в частности графика и процедуры пользовательского интерфейса.
Джим Довей
30
Линус утверждает (явно, как Рик ван Риэль в этом обсуждении) о goto для обработки статуса выхода, и он делает это на основе сложности, которую альтернативные конструкции C привнесут, если их использовать вместо этого.
Чарльз Стюарт
21
ИМХО Линус прав в этом вопросе. Суть его в том, что код ядра, написанный на C, который должен реализовывать нечто похожее на обработку исключений, наиболее четко и просто написан с использованием goto. Идиома goto cleanup_and_exitявляется одним из немногих «хороших» использования Гото осталось теперь у нас есть for, whileи ifуправлять нашим потоком управления. Смотрите также: programmers.stackexchange.com/a/154980
steveha
50

В C gotoработает только в рамках текущей функции, которая стремится локализовать любые потенциальные ошибки. setjmpи longjmpгораздо более опасны, будучи нелокальными, сложными и зависящими от реализации. Однако на практике они слишком неясны и необычны, чтобы вызывать много проблем.

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

3420 IF A > 2 THEN GOTO 1430

Здесь Линус описывает подходящее использование goto: http://www.kernel.org/doc/Documentation/CodingStyle (глава 7).

мин
источник
7
Когда BASIC был впервые доступен, не было никакой альтернативы GOTO nnnn и GOSUB mmmm как способам прыгать. Структурированные конструкции были добавлены позже.
Джонатан Леффлер
7
Вы упускаете суть ... даже тогда вам не нужно было писать спагетти ... ваши GOTO всегда можно использовать дисциплинированно
JoelFan
Также стоит отметить, что поведение setjmp/ longjmpбыло указано только тогда, когда они использовались в качестве средства перехода к точке в пределах области действия из других мест в той же области действия. Как только элемент управления выходит из области действия, в которой setjmpон выполняется, любая попытка использования longjmpсозданной структуры setjmpприведет к неопределенному поведению.
суперкат
9
Некоторые версии BASIC позволят вам сделать GOTO A * 40 + B * 200 + 30. Нетрудно понять, как это было очень удобно и очень опасно.
Джон Ханна
1
@Hjulle будет вычислять выражение и затем переходить к строке кода с этим номером (явные номера строк были требованием большинства более ранних диалектов). ZX Spectrum Basic согласился с этим
Джон Ханна,
48

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

Посчитайте количество gotos в современной программе на Си. Теперь добавьте число break, continueи returnзаявлений. Кроме того, добавьте несколько раз вы используете if, else, while, switchили case. Это примерно столько, сколько у GOTOвашей программы было бы, если бы вы писали на Фортране или Бейсике в 1968 году, когда Дейкстра написал свое письмо.

Языки программирования в то время испытывали недостаток в потоке управления. Например, в оригинальном дартмутском бейсике:

  • IFзаявления не было ELSE. Если вы хотели один, вы должны были написать:

    100 IF NOT condition THEN GOTO 200
    ...stuff to do if condition is true...
    190 GOTO 300
    200 REM else
    ...stuff to do if condition is false...
    300 REM end if
    
  • Даже если ваше IFутверждение не нужно ELSE, оно все равно будет ограничено одной строкой, которая обычно состоит из GOTO.

  • Там не было никакого DO...LOOPзаявления. Для FORнециклов, вы должны были закончить цикл с явным GOTOили IF...GOTOвернуться к началу.

  • Не Существовал нет SELECT CASE. Вы должны были использовать ON...GOTO.

Таким образом, вы в конечном итоге с большим из GOTOс в вашей программе. И вы не могли зависеть от ограничения GOTOs в пределах одной подпрограммы (потому что GOSUB...RETURNбыла такая слабая концепция подпрограмм), так что эти GOTOs могли пойти куда угодно . Очевидно, что это затрудняло контроль потока управления.

Вот откуда GOTOпришло анти- движение.

dan04
источник
Следует также отметить, что предпочтительным способом написания кода, если бы в цикле был какой-то код, который должен был бы выполняться редко, был бы 420 if (rare_condition) then 3000// 430 and onward: rest of loop and other main-line code// 3000 [code for rare condition]// 3230 goto 430. Таким образом, написание кода позволяет избежать любых взятых ветвей или переходов в общем случае основной линии, но это усложняет задачу. Избегание ветвей в ассемблерном коде может быть хуже, если некоторые ветви ограничены, например, +/- 128 байтами и иногда не имеют дополнительных пар (например, существует «cjne», но не «cje»).
суперкат
Однажды я написал код для 8x51, который имел прерывание, которое запускалось каждые 128 циклов. Каждый дополнительный цикл, потраченный в общем случае ISR, снизил бы скорость выполнения основного кода более чем на 1% (я думаю, что 90 из 128 циклов обычно были доступны основной линии), и любая инструкция ветвления могла бы занять два цикла ( как в случае ветвления, так и в случае провала). У кода было два сравнения - одно, которое обычно сообщало бы равное; другой, не равный. В обоих случаях редкий код был длиной более 128 байт. Итак ...
суперкат
cjne r0,expected_value,first_comp_springboard/.../ cjne r1,unexpected_value,second_comp_fallthrough// `ajmp second_comp_target` // first_comp_springboard: ajmp first_comp_target// second_comp_fallthrough: .... Не очень хороший паттерн кодирования, но когда учитываются отдельные циклы, каждый делает такие вещи. Конечно, в 1960-х такие уровни оптимизации были более важными, чем сегодня, тем более что современные процессоры часто требуют странных оптимизаций, а системы компиляции точно в срок могут применять оптимизирующий код для процессоров, которых не было, когда соответствующий код был написан.
суперкат
34

Go To может обеспечить своего рода замену для «реальной» обработки исключений в определенных случаях. Рассматривать:

ptr = malloc(size);
if (!ptr) goto label_fail;
bytes_in = read(f_in,ptr,size);
if (bytes_in=<0) goto label_fail;
bytes_out = write(f_out,ptr,bytes_in);
if (bytes_out != bytes_in) goto label_fail;

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

success=false;
do {
    ptr = malloc(size);
    if (!ptr) break;
    bytes_in = read(f_in,ptr,size);
    if (count=<0) break;
    bytes_out = write(f_out,ptr,bytes_in);
    if (bytes_out != bytes_in) break;
    success = true;
} while (false);

Теперь функционально этот код делает то же самое. На самом деле код, сгенерированный компилятором, практически идентичен. Тем не менее, в стремлении программиста умиротворить Ногото (страшного бога академического упрека) этот программист полностью нарушил основную идиому, которую whileпредставляет цикл, и сделал реальное число для читабельности кода. Это не лучше.

Итак, мораль этой истории в том, что если вы прибегаете к чему-то действительно глупому, чтобы избежать использования goto, то не делайте этого.

tylerl
источник
1
Хотя я склонен согласиться с тем, что вы здесь говорите, тот факт, что breakутверждения находятся внутри контрольной структуры, четко показывает, что они делают. В этом gotoпримере человек, читающий код, должен просмотреть код, чтобы найти метку, которая теоретически могла бы быть перед структурой управления. Я не достаточно опытен в старом стиле C, чтобы судить, что одно определенно лучше другого, но в любом случае есть компромиссы.
Река Вивиан
5
@DanielAllenLangdon: Тот факт, что breaks находятся внутри цикла, ясно показывает, что они выходят из цикла . Это не «именно то, что они делают», так как на самом деле петли нет вообще! Ничто в этом не имеет шансов повториться, но до конца не ясно. Тот факт, что у вас есть «цикл», который никогда не запускается более одного раза, означает, что управляющие структуры злоупотребляют. На gotoпримере программист может сказать goto error_handler;. Это более явно, и даже менее трудно следовать. (Ctrl + F, "error_handler:", чтобы найти цель. Попробуйте сделать это с помощью "}".)
cHao
1
Однажды я увидел код, похожий на ваш второй пример в системе управления воздушным движением - потому что «goto не в нашем лексиконе».
QED
30

Дональд Э. Кнут ответил на этот вопрос в книге «Грамотное программирование», 1992 CSLI. На стр. 17 есть эссе « Структурированное программирование с использованием операторов goto » (PDF). Я думаю, что статья могла быть опубликована и в других книгах.

В статье описывается предложение Дейкстры и описываются обстоятельства, при которых оно действительно. Но он также приводит ряд контрпримеров (проблем и алгоритмов), которые нельзя легко воспроизвести, используя только структурированные циклы.

Статья содержит полное описание проблемы, историю, примеры и контрпримеры.

Bruno Ranschaert
источник
25

Гото считается полезным.

Я начал программировать в 1975 году. Для программистов эпохи 1970-х годов слова «Goto считать вредными» говорили более или менее о том, что новые языки программирования с современными структурами управления стоит попробовать. Мы попробовали новые языки. Мы быстро обратились. Мы никогда не возвращались.

Мы никогда не возвращались, но, если вы моложе, то вы никогда не были там в первую очередь.

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

Лозунги, которые никто не понимает, не очень освещают. Вероятно, лучше всего забыть такие лозунги. Такие лозунги не помогают.

Однако этот конкретный лозунг «Гото считается вредным» обрел собственную жизнь нежити.

Может ли Гото не быть оскорбленным? Ответ: конечно, но что с того? Практически каждый элемент программирования может быть нарушен. boolНапример, скромным злоупотребляют чаще, чем некоторые из нас хотели бы верить.

В отличие от этого, я не могу вспомнить ни одного фактического случая злоупотребления гото с 1990 года.

Самая большая проблема с goto, вероятно, не техническая, а социальная. Иногда кажется, что программисты, которые не знают очень много, считают, что осуждающее goto заставляет их звучать умно. Возможно, вам придется время от времени удовлетворять таких программистов. Такова жизнь.

Самое плохое в goto сегодня - это то, что он недостаточно используется.

THB
источник
24

Привлеченный Джеем Баллоу, добавившим ответ, я добавлю свои £ 0,02. Если бы Бруно Раншарт еще не сделал этого, я бы упомянул статью Кнута «Структурированное программирование с утверждениями GOTO».

Одна вещь, которую я не видел, обсуждалась, это своего рода код, который, хотя и не совсем распространенный, преподавался в учебниках Фортрана. Такие вещи, как расширенный диапазон цикла DO и подпрограммы с открытым кодом (помните, это будет Fortran II, или Fortran IV, или Fortran 66, а не Fortran 77 или 90). По крайней мере, есть вероятность, что синтаксические детали неточны, но концепции должны быть достаточно точными. Фрагменты в каждом случае находятся внутри одной функции.

Обратите внимание, что в превосходной, но устаревшей (и не изданной) книге Kernighan & Plauger «Элементы стиля программирования, 2-й Эдн » содержатся некоторые реальные примеры злоупотребления GOTO из учебников по программированию его эпохи (конец 70-х годов). Однако материал ниже не из этой книги.

Расширенный диапазон для петли DO

       do 10 i = 1,30
           ...blah...
           ...blah...
           if (k.gt.4) goto 37
91         ...blah...
           ...blah...
10     continue
       ...blah...
       return
37     ...some computation...
       goto 91

Одной из причин такой ерунды была старая старомодная перфокарта. Вы можете заметить, что метки (красиво не по порядку, потому что это был канонический стиль!) Находятся в столбце 1 (фактически, они должны были быть в столбцах 1-5), а код - в столбцах 7-72 ​​(столбец 6 был продолжением колонка маркера). Столбцам 73-80 будет присвоен порядковый номер, и существуют машины, которые сортируют колоды перфокарт в порядке порядкового номера. Если ваша программа была на последовательных карточках и вам нужно было добавить несколько карточек (строк) в середину цикла, вам нужно было бы перевыпускать все после этих дополнительных строк. Однако, если вы заменили одну карту на GOTO, вы можете избежать повторного упорядочения всех карт - вы просто подсовываете новые карты в конце процедуры с новыми порядковыми номерами. Считайте, что это первая попытка «зеленых вычислений»

О, вы также можете заметить, что я обманываю, а не кричу - Фортран IV был написан в верхнем регистре как обычно.

Открытая подпрограмма

       ...blah...
       i = 1
       goto 76
123    ...blah...
       ...blah...
       i = 2
       goto 76
79     ...blah...
       ...blah...
       goto 54
       ...blah...
12     continue
       return
76     ...calculate something...
       ...blah...
       goto (123, 79) i
54     ...more calculation...
       goto 12

GOTO между метками 76 и 54 является версией вычисленного goto. Если переменная i имеет значение 1, перейдите к первой метке в списке (123); если оно имеет значение 2, перейдите ко второму и так далее. Фрагмент от 76 до вычисленного goto является подпрограммой с открытым кодом. Это был кусок кода, выполняемый скорее как подпрограмма, но записанный в теле функции. (У Fortran также были операторные функции - встроенные функции, которые помещались в одну строку.)

Существовали худшие конструкции, чем вычисленное goto - вы могли назначать метки переменным, а затем использовать назначенное goto. Googling назначил goto, чтобы сообщить мне, что он был удален из Фортрана 95. Примите участие в революции структурного программирования, о которой можно было бы сказать, что она началась публично, с письмом или статьей Дейкстры «GOTO считают вредным».

Без некоторого знания того, что было сделано в Фортране (и на других языках, большинство из которых по праву попали на обочину), нам, новичкам, трудно понять масштаб проблемы, с которой сталкивался Дейкстра. Черт, я начал программировать только через десять лет после того, как это письмо было опубликовано (но у меня действительно было несчастье программировать на Фортране IV некоторое время)

Джонатан Леффлер
источник
2
Если вы хотите увидеть пример некоторого кода, использующего goto«в дикой природе», вопрос « Требуется: рабочий алгоритм сортировки Бозе-Хиббарда» показывает некоторый (Algol 60) код, опубликованный в 1963 году. Исходный макет не сравнивается с современными стандартами кодирования. Уточненный (с отступом) код все еще довольно непостижим. В gotoведомости там делают сделать (очень) трудно понять , что алгоритм является.
Джонатан Леффлер
1
Будучи слишком молодым, чтобы испытать что-то близкое к перфокартам, было полезно прочитать о проблеме перфорации. +1
Qix - МОНИКА ПРОИЗОШЛА
20

Там нет таких вещей, как GOTO считается вредным .

GOTO - это инструмент, и, как и все инструменты, им можно пользоваться и злоупотреблять .

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

Лично я не использую ни один в типичном коде , но у меня было странное использование как GOTO, так и WITH, которые были оправданы, и альтернативное решение содержало бы больше кода.

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

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

Lasse V. Karlsen
источник
Проблема с GOTO заключается в том, что он нарушает важные инварианты, которые мы принимаем как должное в современных языках программирования. В качестве одного примера, если я вызываю функцию, мы предполагаем, что когда функция завершит работу, она вернет управление вызывающей стороне, либо в обычном режиме, либо через исключительное разматывание стека. Если эта функция неправильно использует GOTO, то, конечно, это уже не так. Это очень сложно рассуждать о нашем коде. Недостаточно тщательно избегать неправильного использования GOTO. Проблема все еще возникает, если GOTO неправильно используется нашими зависимостями ...
Джонатан Хартли
... Таким образом, чтобы узнать, можем ли мы рассуждать о наших вызовах функций, нам нужно изучить каждую строку исходного кода каждой из наших транзитивных зависимостей, проверяя, не используют ли они GOTO. Так что само существование GOTO в языке нарушило нашу способность уверенно рассуждать о нашем собственном коде, даже если мы используем его идеально (или никогда не используем) сами. По этой причине GOTO - это не просто инструмент для бережного использования. Он системно нарушен, и его существование на языке в одностороннем порядке считается вредным.
Джонатан Хартли
Даже если gotoключевое слово было удалено из C #, концепция «перейти сюда» все еще существует в IL. Бесконечный цикл можно легко построить без ключевого слова goto. Если это отсутствие гарантии того, что вызываемый код вернется, по вашему мнению, означает «неспособность рассуждать о коде», то я бы сказал, что у нас никогда не было такой возможности.
Лассе В. Карлсен
Ах, вы хорошо поняли источник нашего недопонимания. В gotoC # не является настоящим "goto" в первоначальном смысле этого слова. Это гораздо более слабая версия, которая позволяет прыгать только внутри функции. Смысл, в котором я использую «goto», позволяет прыгать в любом месте процесса. Таким образом, хотя C # имеет ключевое слово "goto", оно, возможно, никогда не имело, настоящего goto. Да, настоящее goto доступно на уровне IL, так же, как и при компиляции любого языка до сборки. Но программист защищен от этого, не может использовать его в нормальных условиях, поэтому он не считается.
Джонатан Хартли
Между прочим, мнение, которое я здесь описываю, изначально не мое, а является основой оригинальной статьи Дейкстры 1967 года, я думаю, переименованной его редактором «Гото считается вредным», ставшим таким часто цитируемым мемом для 50 годы именно потому, что он был таким революционным, проницательным и общепринятым.
Джонатан Хартли
17

Такого никогда не было, если вы могли думать сами.

stesch
источник
17

Поскольку я начал делать некоторые вещи в ядре Linux, gotos не беспокоит меня так сильно, как раньше. Сначала я был в ужасе от того, что они (ребята из ядра) добавили gotos в мой код. С тех пор я привык к использованию gotos, в некоторых ограниченных контекстах, и теперь буду иногда использовать их сам. Как правило, это переход, который переходит к концу функции, чтобы выполнить какую-то очистку и выручить, а не дублировать ту же самую очистку и выкуп в нескольких местах в функции. И, как правило, это не что-то достаточно большое, чтобы передать другой функции - например, освобождение некоторых локально (k) malloc-переменных является типичным случаем.

Я написал код, который использовал setjmp / longjmp только один раз. Это было в программе MIDI барабанного секвенсора. Воспроизведение происходило в отдельном процессе от всего взаимодействия с пользователем, и процесс воспроизведения использовал общую память с процессом пользовательского интерфейса, чтобы получить ограниченную информацию, необходимую для воспроизведения. Когда пользователь хотел остановить воспроизведение, процесс воспроизведения просто выполнял longjmp «назад в начало», чтобы начать сначала, а не какое-то сложное раскручивание того места, где оно выполнялось, когда пользователь хотел остановить его. Это работало отлично, было просто, и у меня никогда не было проблем или ошибок, связанных с этим в этом случае.

У setjmp / longjmp есть свое место - но это место, которое вы вряд ли посетите, но время от времени.

Изменить: я только что посмотрел на код. На самом деле я использовал siglongjmp (), а не longjmp (не то, чтобы это было важно, но я забыл, что siglongjmp вообще существует).

оборота смкамерон
источник
15

Если вы пишете виртуальную машину на C, то получается, что использование (gcc) вычисленных gotos выглядит так:

char run(char *pc) {
    void *opcodes[3] = {&&op_inc, &&op_lda_direct, &&op_hlt};
    #define NEXT_INSTR(stride) goto *(opcodes[*(pc += stride)])
    NEXT_INSTR(0);
    op_inc:
    ++acc;
    NEXT_INSTR(1);
    op_lda_direct:
    acc = ram[++pc];
    NEXT_INSTR(1);
    op_hlt:
    return acc;
}

работает намного быстрее, чем обычный переключатель внутри цикла.

Vt.
источник
Единственная проблема, что это не стандартно, не так ли?
Кальмарий
&&op_incконечно, не компилируется, потому что (слева) &ожидает lvalue, но (справа) &возвращает rvalue.
fredoverflow
7
@FredO: это специальный оператор GCC. Тем не менее, я бы отклонил этот код во всех случаях, кроме самых тяжелых обстоятельств, потому что я совершенно точно не могу понять, что происходит.
Щенок
Хотя это один из примеров (макс. Оптимизация по скорости), который оправдывает использование как goto, так и загадочного кода, его все же следует прокомментировать, чтобы объяснить необходимость оптимизации, примерно то, как она работает, и лучший построчно комментарии, которые можно сделать. Таким образом, он все еще не работает, но потому что он оставляет программистов обслуживания в темноте. Но мне нравится пример с ВМ. Спасибо.
MicroservicesOnDDD
14

Потому что gotoможет быть использовано для запутывания метапрограммирования

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

Это низкоуровневый в том смысле , что Гото примитивная операция , которая реализует нечто более высокое , как whileи foreachили что - то.

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

Таким образом, есть прозаическая и злая сторона goto.

Прозаическая сторона является то , что направленная вверх Гото может реализовать вполне разумный цикл и направленный вниз Гото может сделать вполне разумным breakили return. Конечно, фактический while, breakили returnбыл бы намного более читабельным, так как бедному человеку не пришлось бы имитировать эффект goto, чтобы получить общую картину. Итак, плохая идея в целом.

Сторона зла включает рутину , не используя Гота для некоторого времени, перерыв, или вернуться, но использовать его для того, что называется спагетти логики . В этом случае разработчик goto-happy строит кусочки кода из лабиринта goto, и единственный способ понять это - это симулировать его мысленно в целом, ужасно утомительная задача, когда есть много goto. Я имею в виду, представьте себе проблему оценки кода, где elseточка не является точной инверсией if, где вложенные ifs могут допускать некоторые вещи, которые были отвергнуты внешним if, и т. Д., И т. Д.

Наконец, чтобы действительно охватить эту тему, мы должны отметить, что по существу все ранние языки, кроме Algol, первоначально делали только отдельные утверждения в соответствии с их версиями if-then-else. Таким образом, единственный способ сделать условный блок состоял в том, чтобы gotoобойти его, используя обратный условный блок . Безумно, я знаю, но я прочитал некоторые старые спецификации. Помните, что первые компьютеры были запрограммированы в двоичном машинном коде, поэтому я предполагаю, что любой тип HLL был спасителем; Я предполагаю, что они не были слишком разборчивы в том, какие именно функции HLL они получили.

Сказав все, что я использовал, вставлял одну gotoв каждую программу, которую я написал «просто чтобы раздражать пуристов» .

DigitalRoss
источник
4
+1 за надоедливых пуристов! :-)
Lumi
10

Отказ в использовании оператора GOTO для программистов - это все равно, что сказать плотнику не использовать молоток, поскольку он может повредить стену, пока он забивает гвоздь. Настоящий программист знает, как и когда использовать GOTO. Я следовал за некоторыми из этих так называемых «структурированных программ». Я видел такой ужасный код, чтобы избежать использования GOTO, чтобы я мог застрелить программиста. Хорошо, в защиту другой стороны, я тоже видел настоящий спагетти-код, и эти программисты тоже должны быть застрелены.

Вот только один небольшой пример кода, который я нашел.

  YORN = ''
  LOOP
  UNTIL YORN = 'Y' OR YORN = 'N' DO
     CRT 'Is this correct? (Y/N) : ':
     INPUT YORN
  REPEAT
  IF YORN = 'N' THEN
     CRT 'Aborted!'
     STOP
  END

-----------------------ИЛИ----------------------

10:  CRT 'Is this Correct (Y)es/(N)o ':

     INPUT YORN

     IF YORN='N' THEN
        CRT 'Aborted!'
        STOP
     ENDIF
     IF YORN<>'Y' THEN GOTO 10
CurtTampa
источник
3
DO CRT 'Это правильно? (Да / Нет): ': ВХОД YORN ДО YORN =' Y 'ИЛИ YORN =' N '; и т. д.
joel.neely
5
Действительно, но, что более важно, настоящий программист знает, когда не следует использовать goto- и знает, почему . Избегать языковой конструкции-табу, потому что так сказал $ program_guru, это и есть определение программирования с использованием груза.
Писквор покинул здание
1
Это хорошая аналогия. Чтобы вбить гвоздь, не повредив подложку, вы не избавитесь от молотка. Скорее, вы используете простой инструмент, известный как гвоздодер. Это металлический штифт с коническим концом, имеющим полую выемку на конце, для надежного соединения с головкой гвоздя (эти инструменты бывают разных размеров для разных гвоздей). Другой, тупой конец пуансона гвоздя поражен молотком.
Kaz
9

«По этой ссылке http://kerneltrap.org/node/553/2131 »

По иронии судьбы, устранение goto привело к ошибке: вызов спин-блокировки был опущен.

Джей Баллу
источник
+1 за «устранение goto внесло ошибку»
QED
7

Первоначальный документ следует рассматривать как «Безусловный GOTO считается вредным». В частности, он защищал форму программирования, основанную на условных ( if) и итеративных ( while) конструкциях, а не на тесте и прыжке, обычном для раннего кода. gotoвсе еще полезен в некоторых языках или обстоятельствах, где не существует соответствующей структуры контроля.

Джон Милликин
источник
6

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

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

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

Всегда возможно сделать это правильно без goto, но в этом и некоторых других случаях Goto на самом деле является лучшим решением, прежде всего для удобочитаемости и удобства обслуживания.

-Адам

Адам Дэвис
источник
5

Одним из современных применений GOTO является компилятор C # для создания конечных автоматов для перечислителей, определяемых возвращаемым доходом.

GOTO - это то, что должно использоваться компиляторами, а не программистами.

Брайан Лихи
источник
5
Как вы думаете, кто именно создает компиляторы?
tloach
10
Компиляторы, конечно!
Seiti
3
Я думаю, он имеет в виду «GOTO - это то, что должно использоваться только кодом, генерируемым компилятором».
Саймон Бьюкен
Это дело против goto. Там, где мы могли бы использовать gotoконечный автомат, закодированный вручную для реализации перечислителя, мы можем просто использовать yieldвместо этого.
Джон Ханна
включить много строк (чтобы предотвратить компиляцию в if-else) с компиляцией по умолчанию для переключения с помощью оператора goto.
М.Казем Ахгари
5

Пока C и C ++ (среди других преступников) не помечают разрывы и продолжаются, goto будет продолжать играть свою роль.

DrPizza
источник
Так что помеченный перерыв или продолжить будет отличаться от Goto, как?
Мэтью Уайтед
3
Они не допускают абсолютно произвольных скачков в потоке управления.
DrPizza
5

Если бы само GOTO было злом, компиляторы были бы злы, потому что они генерировали JMP. Если бы прыжок в блок кода, особенно после указателя, был изначально злым, инструкция RETurn была бы злой. Скорее зло находится в потенциальной возможности злоупотребления.

Иногда мне приходилось писать приложения, которые должны были отслеживать количество объектов, где каждый объект должен был следовать сложной последовательности состояний в ответ на события, но все это было определенно однопоточным. Типичная последовательность состояний, если она представлена ​​в псевдокоде:

request something
wait for it to be done
while some condition
    request something
    wait for it
    if one response
        while another condition
            request something
            wait for it
            do something
        endwhile
        request one more thing
        wait for it
    else if some other response
        ... some other similar sequence ...
    ... etc, etc.
endwhile

Я уверен, что это не ново, но способ, которым я обработал это в C (++), заключался в определении некоторых макросов:

#define WAIT(n) do{state=(n); enque(this); return; L##n:;}while(0)
#define DONE state = -1

#define DISPATCH0 if state < 0) return;
#define DISPATCH1 if(state==1) goto L1; DISPATCH0
#define DISPATCH2 if(state==2) goto L2; DISPATCH1
#define DISPATCH3 if(state==3) goto L3; DISPATCH2
#define DISPATCH4 if(state==4) goto L4; DISPATCH3
... as needed ...

Затем (предполагая, что состояние изначально равно 0), структурированный конечный автомат, приведенный выше, превращается в структурированный код:

{
    DISPATCH4; // or as high a number as needed
    request something;
    WAIT(1); // each WAIT has a different number
    while (some condition){
        request something;
        WAIT(2);
        if (one response){
            while (another condition){
                request something;
                WAIT(3);
                do something;
            }
            request one more thing;
            WAIT(4);
        }
        else if (some other response){
            ... some other similar sequence ...
        }
        ... etc, etc.
    }
    DONE;
}

В зависимости от этого, могут быть CALL и RETURN, поэтому некоторые конечные автоматы могут действовать как подпрограммы других конечных автоматов.

Это необычно? Да. Требуется ли некоторое обучение со стороны сопровождающего? Да. Это обучение окупается? Я думаю так. Можно ли это сделать без GOTO, которые прыгают в блоки? Нет.

оборота Майк Данлавей
источник
1
Избегание языковых особенностей из-за страха является признаком повреждения мозга. Это также прекраснее ада.
Вектор Горгот
4

Я избегаю этого, потому что коллега / менеджер, несомненно, поставит под сомнение его использование либо в обзоре кода, либо когда они наткнуться на него. Хотя я думаю, что он использует (например, случай обработки ошибок) - вы столкнетесь с проблемой другого разработчика, у которого будут какие-то проблемы с ним.

Это того не стоит.

Аардварк
источник
Хорошая особенность блоков Try ... Catch в C # заключается в том, что они заботятся об очистке стека и других выделенных ресурсов (называемых разматыванием стека), поскольку исключение всплывает в обработчике исключений. Это делает Try ... Catch намного лучше, чем Goto, поэтому, если у вас есть Try ... Catch, используйте его.
Скотт Уитлок
У меня есть лучшее решение: обернуть кусок кода, из которого вы хотите выйти, в 'do {...} while (0);' петля. Таким образом, вы можете выпрыгнуть так же, как goto, без дополнительных затрат в цикле try / catch (я не знаю, как в C #, но в C ++ try недорогой, а catch - дорогой, так что кажется, Чрезмерно бросать исключение, где достаточно простого прыжка).
Джим Довей
10
Джим, проблема в том, что это не более чем тупой окольный способ получить goto.
Кодирование в стиле
4

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

У меня был сложный объект, и мне нужно было поработать над ним. Если объект находился в одном состоянии, я мог бы сделать быструю версию операции, в противном случае мне пришлось бы делать медленную версию операции. Дело в том, что в некоторых случаях, в середине медленной операции, можно было понять, что это можно было сделать с быстрой операцией.

SomeObject someObject;    

if (someObject.IsComplex())    // this test is trivial
{
    // begin slow calculations here
    if (result of calculations)
    {
        // just discovered that I could use the fast calculation !
        goto Fast_Calculations;
    }
    // do the rest of the slow calculations here
    return;
}

if (someObject.IsmediumComplex())    // this test is slightly less trivial
{
    Fast_Calculations:
    // Do fast calculations
    return;
}

// object is simple, no calculations needed.

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

Хьюго

Rocketmagnet
источник
Не-GOTO способ будет использовать функцию fast_calculations, которая несет некоторые накладные расходы. Вероятно, не заметно в большинстве случаев, но, как вы сказали, это было критично для скорости.
Кайл Кронин
1
Ну, это вряд ли удивительно. Весь код, имеющий абсолютно безумные рекомендации по производительности, всегда будет нарушать практически все лучшие практики. Лучшие практики - это достижимость и ремонтопригодность, а не выжимание еще 10 миллисекунд или экономия на 5 байтов больше оперативной памяти.
Джонатон
1
@JonathonWisnoski, законное использование goto также устраняет безумное количество спагетти-кода, смешанного с множеством переменных крысы, чтобы отслеживать, куда мы идем.
vonbrand
4

Почти во всех ситуациях, когда можно использовать goto, вы можете сделать то же самое, используя другие конструкции. Goto все равно используется компилятором.

Я лично никогда не использую это явно, никогда не нужно.

Адам Хоулдсворт
источник
4

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

Рассмотрим случай с множеством вложенных циклов, где использование goto вместо набора if(breakVariable)секций, очевидно, более эффективно. Решение «Поместите ваши циклы в функцию и используйте return» часто совершенно неоправданно. В вероятном случае, когда в циклах используются локальные переменные, теперь вам нужно передать их все через параметры функции, потенциально обрабатывая множество дополнительных головных болей, которые возникают из-за этого.

Теперь рассмотрим случай очистки, который я сам использовал довольно часто и который настолько распространен, что предположительно отвечает за структуру try {} catch {}, недоступную во многих языках. Количество проверок и дополнительных переменных, которые требуются для выполнения одного и того же действия, намного хуже, чем одна или две инструкции для перехода, и опять же, решение дополнительной функции вовсе не является решением. Вы не можете сказать мне, что это более управляемо или более читабельно.

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

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

gkimsey
источник
3

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

Брайан Р. Бонди
источник