- goto
-
goto (от англ. go to — «перейти к») — оператор безусловного перехода (перехода к определённой точке программы, обозначенной номером строки либо меткой) в некоторых языках программирования. В некоторых языках оператор безусловного перехода может иметь другое имя (например,
jump
в языках ассемблера).Содержание
Функциональность
Как правило, оператор
goto
состоит из двух частей: собственно оператора и метки, указывающей целевую точку перехода в программе:goto метка
. Метка, в зависимости от правил языка, может быть либо числом (как, например, в классическом Бейсике), либо идентификатором используемого языка программирования. Для меток-идентификаторов метка, как правило, ставится перед оператором, на который должен осуществляться переход, и отделяется от него двоеточием (метка:
).Действие оператора перехода состоит в том, что после его исполнения следующими будут исполняться операторы программы, идущие в тексте непосредственно после метки (до следующего оператора перехода, ветвления или цикла). Для машинных языков инструкция перехода копирует в регистр процессора, содержащий адрес следующей выполняемой команды, адрес команды, помеченной меткой.
Распространение
Оператор
goto
имеется в таких языках, как Фортран, Алгол, Кобол, Бейсик, Си и C++, C#, D, Паскаль, Perl, Ада, PHP и многих других. Он присутствует также во всех языках ассемблера (обычно под названиемjmp
,jump
). Свобода использованияgoto
в разных языках различается. Если в ассемблерах или языках типа Фортрана он может применяться произвольно (допускается передача управления внутрь ветви условного оператора или внутрь тела цикла или процедуры), то в более высокоуровневых языках его использование ограничено: как правило, с помощьюgoto
запрещено передавать управление между различными процедурами и функциями, внутрь выделенного блока операторов, между ветвями условного оператора и оператора множественного выбора.goto
отсутствует в некоторых языках высокого уровня (например, в Форт). В Паскальgoto
первоначально включён не был, но недостаточность имеющихся языковых средств вынудила Никлауса Вирта его добавить. В более поздних своих языках Вирт всё же отказался отgoto
: этого оператора нет ни в Модуле-2, ни в Обероне и Компонентном Паскале. В Java есть зарезервированное словоgoto
, но оно не несёт никаких функций — оператора безусловного перехода в языке нет (однако переход осуществить можно[1]). При этом в языке сохранились метки — они могут применяться для выхода из вложенных циклов операторамиbreak
иcontinue
.Критика
Оператор
goto
в языках высокого уровня является объектом критики, поскольку чрезмерное его применение приводит к созданию нечитаемого «спагетти-кода». Впервые эта точка зрения была отражена в статье Эдсгера Дейкстры «Доводы против оператора GOTO»,[2] который заметил, что качество программного кода обратно пропорционально количеству операторовgoto
в нём. Статья приобрела широкую известность как среди теоретиков, так и среди практиков программирования, в результате чего взгляды на использование оператораgoto
были существенно пересмотрены. В своей следующей работе Дейкстра обосновал тот факт, что для кода безgoto
намного легче проверить формальную корректность.Код с
goto
трудно форматировать, так как он может нарушать иерархичность выполнения (парадигму структурного программирования) и потому отступы, призванные отображать структуру программы, не всегда могут быть выставлены правильно.goto
также мешает оптимизации компиляторами управляющих структур.[3]Некоторые способы применения
goto
могут создавать проблемы с логикой исполнения программы:- Если некоторая переменная инициализируется (получает значение) в одном месте и потом используется далее, то переход в точку после инициализации, но до использования, приведёт к тому, что будет использовано значение, которое находилось в памяти, выделенной под переменную, до момента выделения (и которое, как правило, является произвольным и случайным).
- Передача управления внутрь тела цикла приводит к пропуску кода инициализации цикла или первоначальной проверки условия. Аналогично, передача управления внутрь процедуры или функции приводит к пропуску её эпилога, в котором производится инициализация (выделение памяти под локальные переменные и т. п.).
Доводы против оператора
goto
оказались столь серьёзны, что в структурном программировании его стали рассматривать как крайне нежелательный. Это нашло отражение при проектировании новых языков программирования. Например,goto
был запрещён в Java и Ruby. В ряде современных языков он всё же оставлен из соображений эффективности в тех редких случаях, когда применениеgoto
оправданно. Так,goto
сохранился в Аде — одном из наиболее продуманных с точки зрения архитектуры языков за всю историю.[4] Однако в тех современных языках высокого уровня, где этот оператор сохранился, на его использование, как правило, накладываются жёсткие ограничения, препятствующие использованию наиболее опасных методов его применения: например, запрещается передавать управление извне цикла, процедуры или функции внутрь. Стандарт языка C++ запрещает обход инициализации переменной с помощьюgoto
.Формально доказано (теорема Бёма — Якопини), что применение
goto
не является обязательным, то есть не существует такой программы сgoto
, которую нельзя было бы переписать без него с полным сохранением функциональности (однако, возможно, с потерей эффективности).Оправданное применение
В практическом программировании применение
goto
в некоторых случаях считается допустимым, когда другие средства языка не реализуют или недостаточно эффективно реализуют нужную функциональность.Главным критерием применимости
goto
является ненарушение используемой парадигмы программирования (в приведённых ниже примерах это структурное программирование), в противном случае результат чреват всевозможными побочными эффектами и труднообнаружимыми ошибками.Выход из вложенных циклов
В некоторых языках нет операторов досрочного завершения цикла или они относятся только к тому из вложенных циклов, в котором расположены (например,
break
иcontinue
в Си). Использованиеgoto
для выхода из нескольких вложенных циклов сразу в этом случае значительно упрощает код программы, избавляя от необходимости применения вспомогательных переменных-флагов и условных операторов.Другие варианты решения этой проблемы — помещение вложенных циклов в отдельную процедуру и использование оператора выхода из процедуры, а в языках с поддержкой исключений — генерация исключения, обработчик которого располагается за пределами циклов. Однако подобные решения менее эффективны из-за накладных расходов на реализацию, особенно если соответствующий участок кода вызывается многократно.
Пример на языке Си:
int matrix[n][m]; int value; ... for(int i=0; i<n; i++) for (int j=0; j<m; j++) if (matrix[i][j] == value) { printf("value %d found in cell (%d,%d)\n",value,i,j); //act if found goto end_loop; } printf("value %d not found\n",value); //act if not found end_loop: ;
Прямолинейный способ избавления от
goto
— создать дополнительную переменную-флаг, сигнализирующую, что надо выйти из внешнего цикла (после выхода из внутреннего по break) и обойти блок кода, выполняющийся, когда значение не найдено.Без изменения структуры кода проблема решается, если команда
break
(или её аналог) позволяет выйти из нескольких вложенных блоков сразу, как в Java или Ада. Пример на языке Java:int[][] matrix; int value; ... outer: { for(int i=0; i<n; i++) for (int j=0; j<m; j++) if (matrix[i][j] == value) { System.out.println("value " + value + " found in cell (" + i + "," + j + ")"); break outer; } System.out.println("value " + value + " not found"); }
Обработка ошибок
Если в языке нет средств обработки исключений, то оператор goto может использоваться для прерывания «нормального» выполнения кода и перехода к завершающему коду для освобождения занятой памяти и прочих финальных действий. Пример на языке Си:
int fn (int* presult) { int sts = 0; TYPE entity, another_entity = NULL; TYPE2 entity2 = NULL; if ( !( entity = create_entity() ) ) { sts = ERROR_CODE1; goto exit0; } if ( !do_something( entity ) ) { sts = ERROR_CODE2; goto exit1; } if ( condition ) { if ( !( entity2 = create_another_entity() ) ) { sts = ERROR_CODE3; goto exit1; } if ( ( *presult = do_another_thing( entity2 ) == NEGATIVE ) { sts = ERROR_CODE4; goto exit2; } } else { if ( ( *presult = do_something_special( entity ) == NEGATIVE ) { sts = ERROR_CODE5; goto exit2; } } exit2: if ( entity2 ) destroy_another_entity( entity2 ); exit1: destroy_entity( entity ); exit0: return sts; }
Без goto подобный код был бы излишне загромождён множеством дополнительных условных операторов
if
.Автогенерация кода
Ещё одним допустимым применением безусловного перехода считается код, который генерируется автоматически, например, генерируемые с помощью программных инструментальных средств лексические и синтаксические анализаторы. Так, код, генерируемый утилитами yacc, lex, bison, изобилует командами
goto
, но этот код в принципе не предназначен для восприятия и редактирования человеком, а его корректность целиком определяется корректностью создающего его инструмента.В культуре
В сериале Футурама, в церкви роботов фигурируют афористичные высказывания, оформленные в стиле операторов языка Бейсик:
- В одной из серий свадьба роботов проходила в церкви роботов, над алтарём которой была написана заповедь:
10 SIN
20 GOTO HELL
(греши, потом иди в ад)
- В серии «I, Roommate» в квартире Фрая и Бендера на картине есть надпись:
10 HOME
20 SWEET
30 GOTO 10
(дом, милый [дом, милый…])
Примечания
- ↑ com.sun.org.apache.bcel.internal.generic: public class: GOTO
- ↑ Э. Дейкстра. Доводы против оператора goto
- ↑ Donald Knuth. Structured Programming with go to Statements 1974
- ↑ Code Complete: A Practical Handbook of Software Construction Redmond: Microsoft Press, 1993. 880 p.
Ссылки
- goto в Pascal, Delphi (рус.)
- Carl Youngblood. An Argument for the Use of goto Statements (англ.)
Общие команды MS-DOS и Windows Append • Attrib • Break • Call • Cd • Chcp • Chdir • Chkdsk • Cls • Command • Copy • Date • Debug • Defrag • Del • Dir • Diskcomp • Diskcopy • Doskey • Echo • Edit • Edlin • Erase • Exit • Expand • Fastopen • Fc • Find • For • Format • Goto • Graphics • Help • If • Label • Loadfix • Md • Mem • Mkdir • Mode • More • Move • Nlsfunc • Path • Pause • Print • Prompt • Rd • Rem • Ren • Rename • Replace • Rmdir • Set • Setver • Share • Shift • Sort • Subst • Time • Tree • Type • Ver • Verify • Vol • Xcopy См. также Список команд DOS • Список команд операционных систем Miscrosoft
Категории:- Командная строка MS-DOS/Windows
- Концепции языков программирования
Wikimedia Foundation. 2010.