Стоит ли использовать goto?

29

gotoпочти повсеместно обескуражен. Стоит ли когда-либо использовать это утверждение?

Casebash
источник
16
xkcd.com/292
TheLQ
1
gotoэто то, что в конечном итоге делает процессор, но он плохо работает с тем, что людям нужно понимать и абстрагировать из-за отсутствия отметки на целевом конце. Сравните с Intercal COMEFROM.
2
@ ThorbjørnRavnAndersen, что еще люди имеют в виду, когда говорят о переходах состояний в автомате состояний? Разве ты не видишь сходство? «Перейти в данное состояние », вот что это значит, и все остальное было бы не чем иным, как ненужным осложнением такой тривиальной вещи. И что вы подразумеваете под "без знака"? Этикетка - это такой знак.
SK-logic
@ SK-логика, зависит от того, как далеко вы позволите идти Goto's. Конечные автоматы не требуют перехода к реализации.
@ ThorbjørnRavnAndersen, конечно goto, не требуется. Это просто наиболее рациональный способ их реализации на императивных языках, поскольку он наиболее близок к самой семантике перехода между состояниями. И его назначение может быть довольно далеко от источника, это не будет мешать удобочитаемости. Мой любимый пример такого кода - реализация приключенческой игры DE Knuth.
SK-logic

Ответы:

50

Это несколько раз обсуждалось на переполнении стека, и Крис Гиллум суммировал возможные варианты использованияgoto :

Чисто выход из функции

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

Выход из вложенных циклов

Если вы находитесь во вложенном цикле и хотите выйти из всех циклов, goto может сделать это намного чище и проще, чем операторы break и if-проверки.

Улучшения производительности на низком уровне

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

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

Сообщество
источник
28
Первая проблема решается очень аккуратно с помощью finallyблоков в современных языках, а вторая решается с помощью breaks. Однако, если вы застряли на C, gotoэто практически единственный способ элегантного решения этих проблем.
Чинмай Канчи
2
Очень хорошие очки. Мне нравится, как Java позволяет выходить из нескольких уровней циклов
Casebash
4
@Chinmay - наконец, блоки применяются только к 1) современным языкам, в которых они есть, и б) вы можете допускать накладные расходы (обработка исключений имеет накладные расходы.) То есть использование finallyдопустимо только в этих условиях. Есть очень редкие, но допустимые ситуации, когда goto - это путь.
luis.espinal
21
Ладно, люди ... какая разница между break 3или break myLabelи goto myLabel. О, это верно только ключевое слово. Если вы используете break, continueили какое-то похожее ключевое слово, вы на самом деле используете goto(Если вы используете, for, while, foreach, do/loopвы используете условный переход.)
Мэтью Уитед
4
Что касается производительности низкого уровня - поведение современного процессора может быть сложно предсказать (конвейер, выполнение не по порядку), поэтому, вероятно, в 99% случаев оно того не стоит или даже может быть медленнее. @MatthewWhited: Scoping. Если вы вводите область действия в произвольной точке, неясно (человеку), какие конструкторы следует называть (проблема существует и в Си).
Maciej Piechotka
42

Конструкции потока управления более высокого уровня, как правило, соответствуют понятиям в проблемной области. If / else - это решение, основанное на некотором условии. Цикл говорит, что нужно выполнить какое-то действие повторно. Даже в заявлении о перерыве говорится, что «мы делали это неоднократно, но теперь нам нужно остановиться».

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

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

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

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

Например, в Си я считаю, что есть три основных сценария, в которых уместно использовать goto.

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

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

Основное использование goto на достаточно современном языке (который поддерживает if / else и циклы) - это имитация конструкции потока управления, которая отсутствует в языке.

Кит Томпсон
источник
5
На самом деле это лучший ответ на сегодняшний день - довольно удивительный для вопроса, которому полтора года. :-)
Конрад Рудольф
1
+1 к «лучшему ответу». --- «Основное использование goto на достаточно современном языке (который поддерживает if / else и циклы) - это имитация конструкции потока управления, которая отсутствует в языке».
Дейв Допсон
В конечном автомате оператора switch каждая запись в управляющее значение действительно a goto. SSSM часто являются хорошими структурами для использования, поскольку они отделяют состояние SM от конструкции исполнения, но на самом деле они не устраняют "gotos".
суперкат
2
«При прочих равных коде, структура которого отражает структуру проблемной области, легче читать и поддерживать». Я знал это в течение долгого времени, но мне не хватало слов, чтобы точно передать это так, чтобы другие программисты могли и будут действительно понимать. Спасибо тебе за это.
Wildcard
«Теорема о структурированной программе» меня удивляет, поскольку она наглядно демонстрирует тот факт, что использование операторов структурированного программирования для эмуляции goto гораздо менее читабельно, чем просто использование goto!
11

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

Java попыталась «решить» эту проблему, gotoполностью запретив . Однако Java позволяет вам использовать returnвнутри finallyблока и, таким образом, непреднамеренно проглатывать исключение. Та же проблема все еще существует: компилятор не выполняет свою работу. Удаление gotoиз языка не исправило это.

В C #, gotoявляется таким же безопасным , как break, continue, try/catch/finallyи return. Он не позволяет использовать неинициализированные переменные, не позволяет выпрыгнуть из блока finally и т. Д. Компилятор будет жаловаться. Это потому, что это решает реальную проблему, которая, как я сказал, бессмысленный поток управления. gotoмагически не отменяет анализ с определенным назначением и другие разумные проверки компилятором.

Timwi
источник
Вы считаете, что C # gotoне является злом? Если это так, то так обстоит дело с каждым обычно используемым современным языком с gotoконструкцией, а не только с C # ...
lvella
9

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

Конечно, помеченный breakоператор Java делает то же самое, но не позволяет вам перейти к произвольной точке в коде, что решает проблему аккуратно, не допуская того, что создает gotoзло.

Чинмай Канчи
источник
Кто-нибудь хочет объяснить понижение голосов? Это был совершенно правильный ответ на вопрос ...
Чинмай Канчи
4
Goto никогда не бывает элегантным ответом. Когда ваши циклы вложены в несколько уровней, ваша проблема заключается в том, что вы не смогли спроектировать свой код, чтобы избежать многопоточных циклов. Процедурные вызовы НЕ враг. (Прочтите статьи «Лямбда: предельное ...».) Использование goto для разрыва петель, вложенных в несколько уровней, приводит к наложению пластыря на открытый множественный перелом: это может быть просто, но это неправильно ответ.
Джон Р. Штром
6
И, конечно же, никогда не бывает странного случая, когда требуются глубоко вложенные циклы? Быть хорошим программистом - это не только следовать правилам, но и знать, когда их нарушать.
Чинмай Канчи
2
@ JohnR.Strohm Никогда не говори никогда. Если вы используете в программировании такие термины, как «никогда», вы явно не имеете дела с угловыми случаями, оптимизацией, ограничениями ресурсов и т. Д. В глубоко вложенных циклах goto - это действительно самый чистый и элегантный способ выхода из циклов. Неважно, сколько вы рефакторинг вашего кода.
Суджай Пхадке
@ JohnR.Strohm: Вторая наиболее пропущенная функция при переключении с VB.NET на C #: Doцикл. Зачем? Потому что я могу изменить один цикл, который нуждается в глубоком разрыве в Doцикл и набирать текст, Exit Doа там нет GoTo. Вложенные циклы случаются все время. Разбиение стеков происходит в некоторых случаях.
Иисус Навин
7

Большая часть разочарования происходит от своего рода «религии», которая была создана с гордостью Бога Джикстра, который убедил в начале 60-х о своей неизбирательной силе:

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

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

В частности, первый основной пункт, приведенный выше, больше разрешен, а второй очищен (если вы gotoвышли из блока, стек распаковывается правильно и вызываются все нужные деструкторы)

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

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

Но проблема не в этом for. Это я.

Такие вещи , как break, continue, throw, bool needed=true; while(needed) {...}и т.д., отмечая более маскарада goto , чтобы уйти подальше от скимитаров этих Djikstrarian фанатиков, что -50 лет после изобретения современного laguages- все еще хотят , чтобы их заключенный. Они забыли, о чем говорил Джикстра, они помнят только название его заметки (GOTO считал вредным, и это даже не было его новым заголовком: оно было изменено редактором), а также обвиняют и ругают, ругают и обвиняют каждый конструкт, имеющий эти 4 письмо в последовательности.

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

Эмилио Гаравалья
источник
1
«Такие вещи, как разбить, продолжить, бросить, bool required = true; в то время как (необходимые) {...} и т. Д. Отмечают нечто большее, чем просто маскарад», я не согласен с этим. Я бы предпочел "такие вещи, как перерыв и т. Д. Ограничены Перейти" или что-то в этом роде. Основная проблема с goto заключается в том, что он обычно слишком мощный. В той степени, в которой нынешний гото менее мощный, чем у Дейкстры, ваш ответ меня устраивает.
Мухаммед Алкарури
2
@MuhammadAlkarouri: Я полностью согласен. Вы только что нашли лучшую формулировку, чтобы точно выразить мою концепцию. Моя точка зрения заключается в том, что эти ограничения иногда могут быть неприменимыми, а то, что вам нужно, не в языке. Тогда более «мощная» вещь, менее специализированная, это то, что позволяет вам обойти. Например, Java break nнедоступна в C ++, следовательно goto escape, даже если escapeне требуется просто выходить из цикла n-ple.
Эмилио Гаравалья
4

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

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

Последнее, что я вставил в фрагмент кода C, - это построение пары взаимосвязанных циклов. Он не вписывается в обычное определение «приемлемого» использования, но в результате функция стала значительно меньше и понятнее. Чтобы избежать goto, потребовалось бы особенно грязное нарушение DRY.

blucz
источник
1
Ответ на этот вопрос наиболее содержательно изложен в старом лозунге «Картофельные чипсы Лея»: «Спорим, ты не можешь есть только один». В вашем примере у вас есть две «взаимосвязанные» (что бы это ни значило, и я держу пари, что это не красиво) петли, и goto дал вам дешевый выход. Как насчет парня из техобслуживания, который должен изменить этот код после того, как тебя сбила шина? Собирается ли Гото сделать его жизнь проще или сложнее? Нужно ли переосмыслить эти две петли?
Джон Р. Штром
3

Я думаю, что весь этот вопрос был случай лая неправильное дерево.

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

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

Единственный случай, когда я на самом деле использовал его в течение многих лет, - это случай цикла, когда точка принятия решения находится в середине цикла. У вас остается либо дублированный код, флаг или GOTO. Я нахожу решение GOTO лучшим из трех. Здесь нет пересечения линий контроля потока, это безвредно.

Лорен Печтель
источник
Случай, на который вы ссылаетесь, иногда называют «цикл с половиной» или «N плюс одна половина цикла», и это «известный пример использования для gotos, которые переходят в цикл (если язык позволяет это; так как в другом случае, прыжок вне цикла в середине, обычно тривиально без goto).
Конрад Рудольф
(Увы, большинство языков запрещают прыгать в петлю.)
Конрад Рудольф
@KonradRudolph: Если вы реализуете «цикл» с помощью GOTO, нет другой структуры цикла, в которую можно было бы перейти.
Лорен Печтел
1
Я думаю , gotoкак выкрикивая КОНТРОЛЬ РАСХОД ЗДЕСЬ НЕ FIT НОРМАЛЬНЫХ СТРУКТУРИРОВАННОГО-ПРОГРАММИРУЮЩИЕ РИСУНКИ . Поскольку потоки управления, которые соответствуют шаблонам структурированного программирования, легче рассуждать, чем те, которые этого не делают, следует по возможности использовать такие шаблоны; если код соответствует таким шаблонам, не следует кричать, что это не так. С другой стороны, в случаях, когда код действительно не соответствует таким шаблонам, кричать об этом может быть лучше, чем притворяться, что код подходит, когда это действительно не подходит.
суперкат
1

Да, goto можно использовать для получения опыта разработчика: http://adamjonrichardson.com/2012/02/06/long-live-the-goto-statement/

Однако, как и в случае с любым мощным инструментом (указатели, множественное наследование и т. Д.), Его нужно дисциплинировать, используя его. В приведенном в ссылке примере используется PHP, который ограничивает использование конструкции goto той же функцией / методом и отключает возможность перехода в новый блок управления (например, цикл, оператор switch и т. Д.).

AdamJonR
источник
1

Зависит от языка. Например, он до сих пор широко используется в программировании на Cobol. Я также работал над устройством Barionet 50, язык программирования микропрограмм которого является ранним бейсиковым диалектом, который, конечно, требует от вас использования Goto.

user16764
источник
0

Я бы сказал нет. Если вы обнаружите необходимость использования GOTO, держу пари, что есть необходимость переделать код.

Вальтер
источник
3
Возможно, но вы не предоставили никаких аргументов
Casebash
Дейкстра подробно изложил свои соображения десятилетия назад.
Джон Р. Штром
2
Просто Догма. Нет резонов. Примечание: Джикстра больше не является ДЕЙСТВУЮЩИМ РЕЗОНОМ: это было упоминание об ОСНОВНОМ или FORTRAN GOTO утверждении начала 60-х. Они не имеют ничего общего с действительно анемичными (по сравнению с силой этих) современниками.
Эмилио Гаравалья
0

gotoможет быть полезно при переносе старого кода ассемблера на C. В первом случае преобразование инструкции за инструкцией в C, использующее gotoв качестве замены branchинструкцию на ассемблере , может позволить очень быстрое перенос.

Грэм Борланд
источник
-1

Я бы сказал, что нет. Их всегда следует заменять инструментом, более специфичным для проблемы, для решения которой используется goto (если она недоступна на вашем языке). Например, операторы break и исключения решают ранее решенные проблемы выхода из цикла и обработки ошибок.

Fishtoaster
источник
Я бы сказал, что когда языки имеют обе эти функции, они gotoобычно отсутствуют в этих языках.
Чинмай Канчи
Но это потому, что они им не нужны или потому, что люди думают, что они им не нужны?
Майкл К
Ваши взгляды фанатичны. Есть много случаев, когда gotoнаиболее близкая вещь к семантике предметной области очень проблемна, и все остальное было бы грязным хаком.
SK-logic
@ SK-логика: я не думаю, что слово «фанатичный» означает, что вы думаете, что это значит.
Кит Томпсон
1
@Keith, «нетерпимо преданный своему собственному мнению и предрассудкам» - вот то, что я постоянно наблюдаю здесь, с этой сногсшибательной партией с завязанными глазами. «Должны быть всегда заменены» - по крайней мере, слова фанатика. Ничего близкого к правильному, здравому мнению, но просто фанатизм. Это «всегда» не оставляет места для альтернативного мнения, понимаете?
SK-logic