goto в коде программы

Запретный плод GOTO сладок!

Небольшой исторический экскурс

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

А все начиналось с комбинационных схем

goto в коде программы. b2455977. goto в коде программы фото. goto в коде программы-b2455977. картинка goto в коде программы. картинка b2455977. Тем, кто и без меня прекрасно знает, что такое комбинационная схема, схема с памятью, и как из этого вырос ассемблер – можно смело перескакивать далее – к выводу.
Вначале было слово – и слово это было функция. Не так уж и важно, что это была булева функция от логической переменной – потом в этом базисе умудрились реализовать всю (почти) математику, а потом и тексты, графику… Как бы то ни было, оказалось, что с помощью вычислительной техники очень удобно делать арифметические, а потом тригонометрические и прочие действия и находить значения функций от переменной.

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

Для решения этой сложнейшей задачи строился последовательный алгоритм для выполнения арифметических операций (в случае заданной точности вычислений в таком алгоритме каждое арифметическое действие можно выполнять за один такт).

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

Но потом вмешались схемы с памятью

goto в коде программы. 99d64404. goto в коде программы фото. goto в коде программы-99d64404. картинка goto в коде программы. картинка 99d64404. Тем, кто и без меня прекрасно знает, что такое комбинационная схема, схема с памятью, и как из этого вырос ассемблер – можно смело перескакивать далее – к выводу.
А потом чья-то умная голова додумалась до схемы с обратной связью – например, RS-триггер. И тогда появилось состояние схемы. А состояние – это ни что иное, как текущее значение всех элементов с памятью.

Появление таких элементов памяти позволило сделать революционный скачок вперед от жестко заданных устройств к микропрограммным автоматам. Упрощенно говоря, в микропрограммных автоматах есть память команд. Есть отдельное устройство, которое реализует текущую микропрограмму (сложение, вычитание или еще чего). А вот выбором «текущей» микропрограммы занимается отдельное устройство – пусть это будет «устройство выборки».

Вопрос – тут есть какие-нибудь переходы? Однозначно да! Более того, появляются переходы безусловные (адрес следующей команды не зависит от текущего состояния данных) и условные (адрес следующей команды зависит от состояния данных).

Можно ли без них обойтись? Да никак! Если не использовать переходы, то мы вернемся к комбинационной схеме без памяти.

В итоге мы пришли к ассемблеру

Апофеозом таких вычислительных устройств стали микро-, просто- и супер-компьютеры. Все они в основе имеют язык кодов, достаточно легко преобразуемый в Ассемблер с приблизительно совпадающим набором команд. Возьмем самый массовый персональный компьютер с Ассемблером от i386 процессора – благо, Windows XP написан для него. Как у него построена работа?

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

Какой итог можно подвести? На уровне микропроцессора операции перехода используются очень активно. Реальную программу, их не использующую, написать почти невозможно (может быть, ее можно сделать, но это будет супер-мега-извращение и точно уж не реальная программа!). С этим тоже спорить никто не будет.

Но почему же тогда в языках более высокого уровня – сконцентрируемся на С/С++ — оператор goto вдруг впал в немилость.

Немного об алгоритмах

goto в коде программы. 3b25af0c. goto в коде программы фото. goto в коде программы-3b25af0c. картинка goto в коде программы. картинка 3b25af0c. Тем, кто и без меня прекрасно знает, что такое комбинационная схема, схема с памятью, и как из этого вырос ассемблер – можно смело перескакивать далее – к выводу.
А теперь посмотрим на хитровывернутый алгоритм. Представление не имею что это за бред – но его надо реализовать.

UPD: здесь A, B, C, D, E — это некоторые операции, а не вызов функции! Вполне возможно, что они используют массу локальных переменных. И вполне возможно, что они меняют их состояние. Т. е. в данном случае речь не идет о вызове функций — некоторые действия, не будем детализировать.

Вот как это выглядит в С с goto:

Очень лаконично и читабельно. Но — нельзя! Попробуем без goto:

Вы что-нибудь поняли из логики работы второго листинга.
Сравним оба листинга:

Но зато во втором листинге нет goto!

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

goto в реальных программах

Я за свой более чем 20-летний стаж прошел несколько аппаратных платформ и с десяток языков программирования, участвовал в написании крупного программного продукта ActiveHDL, делал коммерческую базу данных и много небольших программ для отладки оборудования, используемого в Олимпийских играх, а также делал устройства для этой самой Олимпиады (уже несколько Олимпиад, если быть точным). Короче, что-то я в программировании шарю. А, да, забыл – я закончил с почетным дипломом ХНУРЭ — то бишь, в теории я тоже секу.

Поэтому мои последующие размышления и ситуации… скажем так, я имею моральное право на них.

Неявное использование goto

В языке С есть много операторов, которые на самом деле являются банальным goto – условным или безусловным. Это все виды циклов for (…), while (…) <…>, do <…>while (…). Это анализ числовых переменных switch (…) . Это те же операторы прерывания/перехода в циклах break и continue. В конце концов, Это вызовы функций funct() и выход из них return.

С++ добавляет массу невидимых переходов перегрузками, конструкторами и деструкторами. Из явных переходов – try <… throw …>catch (…) <…>.

Эти goto считаются «легальными» — чем же нелегален сам goto?

В чем обвиняют goto

Обвиняют его в том, что код становится нечитабельным, плохо оптимизируемым и могут появиться ошибки. Это про практические минусы. А теоретические – это просто плохо и неграмотно, и все тут!

Насчет нечитабельности кода и плохой оптимизируемости – еще раз взгляните на листинги выше.
Насчет вероятности появления ошибок – согласен, такой код воспринимается несколько сложнее из-за того, что мы привыкли читать листинг сверху вниз. Но и все! А что, другие средства С++ безопасные и не могут создать ошибок в коде? Приведу краткий список самых опасных вещей С++ («опасные» в том смысле, что с их помощью можно создать массу трудноуловимых ошибок): преобразования типов, перегрузка операторов, указатели и динамическое распределение памяти, наследование классов, шаблоны… Кажется, я перечислил почти все достоинства С++… А, ну да, полиморфизм не назвал – он у меня проходит под «перегрузкой операторов».

Тоже самое и goto. Пользоваться им надо с умом – и тогда код будет работать корректно.

А про теоретические доводы – это, уж простите меня, спор о вкусах. Вы пользуетесь Венгерской нотацией? Я – нет, терпеть ее не могу! Но я ж не говорю, что она плохая из-за этого! Лично я считаю, что переменная должна нести смысловую нагрузку – для чего она создана. Но я не буду запрещать пользоваться этим способом именования другим людям!

Или же есть эстеты, которые считают, что писать a = ++i неграмотно, надо писать i = i + 1; a = i. И что теперь, запретить и это тоже?

Впрочем, есть в C++ ньюанс, где goto приводит к проблемам:

Чему будет равно p? А компилятор его знает! Впрочем, хороший компилятор такого не допустит – или допустит в том случае, если это p нигде не используется.

В то же время выход из цикла вызывает необходимые деструкторы – проверено в Visual Studio 2008.
Что на это скажешь? А то, что не-использование goto не застрахует Вас от неправильно написанной программы. Так же и применение его не гарантирует, что программа будет сыпаться. Нужно писать программы с умом и не делать таких элементарных глюков.

Обработка ошибок

В С++ по сравнению с С появилась замечательная возможность try … throw … catch. Она позволяет эффективно обрабатывать ошибки и прочие сложные ветвления. В С++ — да, но не в С. В С приходится в таком случае использовать goto. Разумеется, можно использовать всяческие флаги и прочее. Но этот вопрос мы уже обсудили – код раздувается, появляются источники новых ошибок, листинг становится нечитабельным.

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

Выход из вложенного цикла наружу

Посмотрите на программу ниже:

Что происходит – понятно? Есть вложенный цикл. Если наступило какое-то условие – покидаем все последующие обработки.

Данный код с флагами выглядит иначе:

Что произошло в данном случае? На каждой итерации мы теперь проверяем флаг. Не забываем его проверять и дальше. Это мелочи, если итераций немного и речь идет о «безразмерной» памяти у PC. А когда программа написана для микроконтроллера – это все уже становится существенно.

Кстати, в связи с этим в некоторых языках (если не ошибаюсь, в Java) есть возможность выйти из цикла по метке вида break Leave. Тот же goto, между прочим!

Точно такой же пример я могу привести и с обработкой в switch (…) < case …>. С этим я сталкиваюсь часто при обработке входящих пакетов неодинаковой структуры.

UPD: как мне справедливо подсказали, в данном случае это проблема для С, а не С++. В С++ можно «выскользнуть» наружу с помощью throw… catch…. Но мы помним, что throw… catch… — тот же самый goto!

Автоматическое создание кода

Знакомы ли Вы с автоматным программированием? Или любым другим автоматизированным созданием кода? Скажем, создатели лексических обработчиков (без использования громоздкого boost::spirit). Все эти программы создают код, который можно использовать как «черный ящик» — Вам не важно, что там внутри; Вам важно, что он делает. А внутри там goto используется очень и очень часто…

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

Выход в одном месте

На С иногда приходится писать что-то вроде:

Этот код гораздо аккуратней будет выглядеть так:

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

Это моя точка зрения! И она справедлива для меня. Может – и для Вас, но я не буду Вас заставлять ей следовать!

Так вот, для меня очевидно, что goto помогает оптимальней и качественней решить некоторые проблемы.
А бывает и наоборот – goto может породить массу проблем.

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

Кто еще подскажет плюсы/минусы? Впишу, если они будут оправданы.

Источник

Оператор GoTo

Безусловно подразделяется на указанную строку в процедуре.

Синтаксис

Отделение

line
Обязательный элемент. Метка любой линии.

Remarks

GoTo Оператор может создать ветвь только для строк в процедуре, в которой она отображается. Строка должна иметь метку, которая GoTo может ссылаться на. Дополнительные сведения см. в разделе инструкции. Метки.

GoTo инструкции могут усложнить чтение и поддержку кода. Везде, где это возможно, следует использовать структуру элементов управления. Дополнительные сведения см. в разделе Поток управления.

Ветвление и конструкции try

Блок или областьВетвление внеВетвление из внутрь
Try блокТолько из Catch блока одной конструкции 1Только за пределами всей конструкции
Catch блокНикогда не разрешеноТолько за пределами всей конструкции или с Try блоком той же конструкции 1
Finally блокНикогда не разрешеноНикогда не разрешено

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

goto в коде программы. try construction branching. goto в коде программы фото. goto в коде программы-try construction branching. картинка goto в коде программы. картинка try construction branching. Тем, кто и без меня прекрасно знает, что такое комбинационная схема, схема с памятью, и как из этого вырос ассемблер – можно смело перескакивать далее – к выводу.

Пример

В следующем примере оператор используется GoTo для перехода к меткам линии в процедуре.

Источник

GOTO в прикладном программировании

goto в коде программы. image loader. goto в коде программы фото. goto в коде программы-image loader. картинка goto в коде программы. картинка image loader. Тем, кто и без меня прекрасно знает, что такое комбинационная схема, схема с памятью, и как из этого вырос ассемблер – можно смело перескакивать далее – к выводу.
Картинка из книги Thinking Forth

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

Основной тезис: в прикладном программировании GOTO однозначно лучше обходить.

GOTO — свойства и влияние на качество кода

Параметры качества кода
Общие свойства GOTO:
Что не является GOTO:

Особенности GOTO в прикладном программировании

Прикладное программирование здесь — программирование на языках высокого уровня, поддерживающих структурирование кода, в том числе структурный подход к обработке исключений: Java, C#, C++, интерпретируемые языки и т.п. — в общем, стандартный прикладной мэйнстрим. C не рассматриваю как низкоуровневый язык, используемый сейчас в основном для системного программирования.

Особенности прикладного программирования:
GOTO только ухудшает сопровождаемость кода

В системном программировании важна максимальная экономия ресурсов, поэтому там, возможно, применение GOTO для этой цели оправдано.
А в прикладном программировании параметр «потребление ресурсов» можно отбросить, остается только параметр сопровождаемости, который GOTO ухудшает.

GOTO — проблемы и варианты исправлений

Рассмотрим применение GOTO в различных вариантах перемещения по структуре кода и альтернативы ему:

1. Вход в блок извне:
1.1 Вход в «не цикл»:

легко и очевидно переписывается без GOTO:

1.2 Вход в цикл:

нельзя: вообще непонятен поток выполнения:

2. Переход внутри одного блока:

нет необходимости, легко переписывается, обычно на if/else:

3. Выход из блока наружу

Это основной случай возможного применения GOTO. Разобьем его на еще более мелкие и рассмотрим подробно на примерах.
Общий подход — максимально декомпозируем: разбиваем на методы по смыслу, логику фиксируем в флагах с говорящими названиями — получаем читабельный и самодокументированный код.

Важные правила:

1) исключения всегда используем для обработки ошибок и внештатных ситуаций, поэтому не используем их для чего-либо еще, чтобы не мозолить глаз;

2) можем случайно «проглотить» исключение с внутреннего уровня вложенности;

3.1. Единственный выход из одного уровня вложенности:

тривиально заменяется if/break и т.п.

3.2. Несколько выходов из одного уровня вложенности:
3.2.1 Обработка ошибок — только через исключения

(надеюсь, это очевидно; если нет — могу объяснить в отдельной статье)

3.2.2 Перебор вариантов — на примере if:

Проблемы (кроме всегда присущей GOTO неочевидности потока выполнения):
захотели добавить поведение sleep в случаях wannaEat и wannaDance — все, обобшение для wannaEat и wannaDrink разрушено.

Как сделать красиво (сразу расширенный вариант):

3.3. Выход из нескольких уровней вложенности.
3.3.1 Если легко выделить разную логику (разные ответственности):
3.3.2 Сложнее выделить разную логику или при этом усложняется код.

Как правило, это может быть в случае однотипных вложенных циклов:

Это — единственный вариант, который смотрится хуже, чем GOTO, и GOTO даже понятнее. Но практически всегда есть и другие варианты.

Для оставшегося исчезающе малого процента случаев, когда других вариантов нет, нужно просто решить, что все равно можно сделать хотя бы флагами, зато гайдлайны будут проще — «Без GOTO!».

Резюме:

Важнее всего — сопровождаемость.

GOTO всегда ухудшает сопровождаемость, поэтому

Источник

GOTO or not GOTO вот в чём вопрос

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

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

Статья посвящается Зацепину П.М., выдающемуся инженеру Алтайского государственного университета, под чьим чутким руководством многие студенты, включая автора статьи, постигали магию инженерного творчества.

Введение

Спор о возможности использования в программах оператора GOTO ведётся уже очень давно (официальным его началом признана статья Дейкстры «О вреде оператора GOTO», опубликованная в 1968 году [2]). Через три года мы будем праздновать 50-летний юбилей этого спора. Это хороший повод, чтобы наконец-то «расставить все точки над i» и прекратить спор.

Цитата в эпиграфе выбрана неслучайно. Она в точности отражает текущую ситуацию в споре про GOTO. В нашем случае «комната за закрытой дверью» – это понятная всем постановка задачи. Пока, к сожалению, такой постановки задачи озвучено не было, поэтому споры и не угасают. Противоборствующие стороны спорят хоть и о схожих, но всё-таки о разных вещах, поэтому и не могут найти компромисса.

Давайте займём в этом споре нейтральную сторону, и беспристрастно во всём разберёмся. Рассмотрим доводы «противников» и «защитников» оператора GOTO и решим, «кто из них прав, а кто виноват».

Почему ведутся споры

Как уже было отмечено выше, споры о возможности использования в программах оператора GOTO ведутся из-за отсутствия понятной всем постановки задачи. Грубо говоря, одна из сторон доказывает, что дерево плавает, а другая, что кирпич тонет. Естественно, что при такой постановке каждая из сторон уверена в своей правоте и будет вечно её отстаивать.

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

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

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

Озвученная точка зрения весьма поверхностна, т.к. не учитывает деталей спора. Чтобы сформулировать объективную постановку задачи, необходимо рассмотреть аргументы и контраргументы каждой из сторон. Этим мы сейчас и займёмся. Жирные буквы З – это аргументы защитников GOTO, а жирные буквы П – это аргументы противников GOTO.

Доводы «противников» GOTO

З: Если на то пошло, то из языка можно выкинуть практически все операторы.

С точки зрения структурного программирования из языка можно вообще выкинуть все операторы, оставив только while и оператор присваивания. [1] В таком случае программы будут хоть и объёмными, но понятными. Если бы на практике внимание уделялось только структуре программы, то такой шаг был бы обоснованным, но в реальных задачах есть ещё требования на быстродействие и компактность, а этого одним оператором добиться невозможно.

GOTO – признак не кривизны кода, а кривизны языков, в которых без него порой никак (C, C++, C#, Pascal, Java, etc) и кривизны профанации под названием «структурное программирование» с его т.н. «циклами с предусловиями», «циклами с постусловиями» и «ветвлениями», которые являются не элементарными конструкциями, а типовыми паттернами, в которые задача не всегда удобно ложится.

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

З: Авторитетные мнения достойны внимания, но то, что говорят авторитеты, не есть истина в последней инстанции. Недаром в учёной среде бытует фраза «Если уважаемый учёный говорит, что «это сделать возможно», то он скорее всего прав, а если говорит, что «это сделать невозможно», то скорее всего не прав».

Есть и такие авторитеты, которые высказываются в пользу GOTO, например, Дональд Кнут [4], Фредрик Брукс. [5] Но при решении задачи более целесообразно опираться не на мнение авторитетов, а на здравый смысл.

Доводы «защитников» GOTO

П: В данном случае GOTO структуру программы не портит, но в таком построении нет практической необходимости, т.к. то же самое можно организовать через if/else.

З: Заменить приведённый код на if/else можно только в том случае, если перед завершением не выполняется дополнительных операций.

П: Дополнительные операции можно вынести в отдельную функцию и вызывать её в каждой ветке.

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

П: Отдельную функцию можно оформить в виде inline-функции, тогда на быстродействии это никак не скажется.

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

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

П: Приведённый код выполняется с той же скоростью и занимает столько же памяти, что и код с GOTO.

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

Подведём итоги

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

Плохой тон в программировании? Если учитывать только «тон структурирования», то да. Но ведь ещё есть «тон быстродействия» и «тон компактности», поэтому нужно искать компромисс между ними. [6] Программисты «работающие в песочнице», как правило, решают задачи, в которых не приходится задумываться об экономии ресурсов, отсюда и вытекает недопонимание.

Игнорировать «тон структурирования» тоже нельзя, т.к. программу в любом случае придётся дорабатывать, и если в ней будет запутанная структура, то возникнут ненужные затраты. Как и в любых других практических решениях оптимальным является компромиссный вариант: использование GOTO разрешено, но со следующими оговорками:

Благодарности

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

Благодарю Дмитрия Леонова за создание сайта bugtraq.ru и за то, что ему удалось сплотить большое количество высококлассных специалистов на своём форуме. Именно на этом форуме развернулась самая интересная дискуссия. Благодарю людей, принявших участие в дискуссии на этом форуме:

Благодарю OlegY, Heller, Zef за примеры кода, где использование GOTO оправдано.

Благодарю HandleX за философские мысли о нужности GOTO при решении практических, а не теоретических задач.

Благодарю amirul за озвучивание правил применения GOTO.

Благодарю AMMOmium за мысль о «байках-страшилках» для начинающих программистов.

Благодарю команду программистов с форума codenet.ru за показательный пример классического спора, а именно следующих лиц: nilbog, koderAlex, OlgaKr, kerdan, kosfiz3A3-968M, IL84, fanto, Sanila_san, nixus, green, newonder.

PS. Спасибо за внимание. Буду рад комментариям; вопросы и возражения также приветствуются.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *