Вопрос

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

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

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


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

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

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

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

Это было полезно?

Решение

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

  1. Неограниченное использование адресов памяти (как GOTO, так и необработанных указателей) предоставляет слишком много возможностей для совершения ошибок, которых легко избежать.
  2. Чем больше существует способов добраться до определенного «места» кода, тем менее можно быть уверенным в том, каково состояние системы в этот момент.(См. ниже.)
  3. ИМХО, структурированное программирование заключается не столько в том, чтобы «избегать GOTO», сколько в том, чтобы структура кода соответствовала структуре данных.Например, повторяющаяся структура данных (т.е.массив, последовательный файл и т. д.), естественно, обрабатывается повторяющейся единицей кода.Наличие встроенных структур (например,while, for, Until, 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
}

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

Другие советы

XKCD's GOTO Comic

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

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

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

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

return;

err_cleanup:
...

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

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

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

ENDOFLOOPS:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

-

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

-

Линус:«Конечно, на глупых языках, таких как Паскаль, где этикетки не могут быть описательными, Goto's может быть плохим».

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

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

3420 IF A > 2 THEN GOTO 1430

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

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

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

В языках программирования в то время отсутствовал поток управления.Например, в оригинальном Dartmouth BASIC:

  • 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в вашей программе.И вы не могли зависеть от ограничения GOTOс точностью до одной подпрограммы (потому что GOSUB...RETURN была такая слабая концепция подпрограмм), поэтому эти GOTOможно пойти в любом месте.Очевидно, это затрудняло отслеживание потока управления.

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

В некоторых случаях 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, не делайте этого.

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

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

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

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

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

Обратите внимание, что превосходная, но устаревшая (и распроданная) книга «Элементы стиля программирования, 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 — это версия вычисляемого перехода.Если переменная i имеет значение 1, перейдите к первой метке в списке (123);если оно имеет значение 2, перейдите ко второму и так далее.Фрагмент от 76 до вычисленного перехода является подпрограммой с открытым кодом.Это был фрагмент кода, исполняемый как подпрограмма, но записанный в теле функции.(В Фортране также были операторные функции, которые представляли собой встроенные функции, помещавшиеся в одну строку.)

Были конструкции и похуже, чем вычисляемый переход: можно было присвоить метки переменным, а затем использовать назначенный переход.Гугление назначен переход сообщает мне, что он был удален из Фортрана 95.Одним из них является революция структурированного программирования, которая, можно сказать, началась публично с письма или статьи Дейкстры «GOTO считается вредным».

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

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

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

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

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

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

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

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

Напротив, я не могу припомнить ни одного реального случая злоупотребления goto с 1990 года.

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

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

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

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

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

Лично я тоже не использую в типичном коде, но у меня были странные случаи использования обоих ИДТИ К и С это было оправдано, и альтернативное решение содержало бы больше кода.

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

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

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

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

У setjmp/longjmp есть свое место, но это место вы вряд ли посетите, но лишь раз в очень долгое время.

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

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

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

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;
}

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

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

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

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

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

Итак, существует прозаический и зло сторона к goto.

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

А злая сторона включает в себя процедуру, не использующую goto для while, Break или возврата, а использующую его для того, что называется логика спагетти.В этом случае разработчик, довольный переходом, конструирует фрагменты кода из лабиринта переходов, и единственный способ понять это — мысленно смоделировать его целиком, а это ужасно утомительная задача, когда переходов много.Я имею в виду, представьте себе проблемы с оценкой кода, в котором else не является точной инверсией if, где вложен ifs может позволить некоторые вещи, которые были отвергнуты внешним if, и т.д. и т.п.

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

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

Отказывать программистам в использовании оператора 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

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

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

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

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

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

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

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

-Адам

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

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

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

Если бы 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, которые переходят в блоки?Неа.

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

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

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

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

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 здесь был оправдан.

Хьюго

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top