Это сайт — моя персональная записная книжка. Интересна мне, по большей части, история, своя жизнь и немного программирование.

Goto

Нередко в разговорах программистов всплывает тема оператора «goto». Например, сообщество крупно побурлило после появления его в ПХП. Многие считают, что оператор вредный, его появление в языке «понижает» язык в классе до Бейсика (хотя современные Бейсики — очень мощные языки) и он несёт только вред.

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

Попробую возразить.

Во-первых, если считать, что присутствие упомянутого оператора ставит язык в один ряд с худшими Бейсиками, то туда же низбежно попадают Ассемблеры и Си — языки, программировать на которых никогда зазорно не было (в Ассемблере goto называется иначе, но суть абсолютно та же).

Во-вторых, во многих языках goto уже есть. Только у него разные названия для разных целей, обычно это break и continue. Они всегда были в ПХП, есть они и в Перле, ПХП, Пайтоне, Джаве и многих других языках.

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

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

Так вот, всего этого современный оператор просто не умеет. Современный goto — тёзка того старого, плохого, сейчас он умеет переходить на метку, но не на всякую, а с огромным количеством ограничений и его использование не более вредно или стыдно, чем использование break или continue.

19 комментариев
hshhhhh (hshhhhh.name) 2014

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

Денис Попов (besisland.name) 2014

Так правильно, break и continue — это частные случаи goto; break и continue — это очень плохо, и нужно стараться их никогда-никогда не использовать.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для besisland.name:

Это утверждение лучше бы смотрелось в паре с аргументом.

SiMM (mr-simm.livejournal.com) 2014

Комментарий для besisland.name:

break и continue — это частные случаи goto; break и continue — это очень плохо, и нужно стараться их никогда-никогда не использовать

Что может привести к ещё большей запутанности кода, чем даже с goto.

SiMM (mr-simm.livejournal.com) 2014

Упс… понял, что могу быть понят двусмысленно — неиспользование break и continue может привести к ещё более нечитаемому коду, чем даже использование goto.

Иван 2014

Денис Попов идеально подходит на роль аналитика, поведение которого описал Евгений.
«Это плохо, ужасно плохо», а что именно плохо -​-​ загадка.

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

Svan 2014

А ещё try-catch-throw — это вообще ужосѣ

PastorGL 2014

ну ок.

goto — это jump в чистом виде.
break/continue — это первая производная от goto, jump с кучей проверок.
try/catch/throw/finally — опять всё то же самое goto, только уже с объектным душком и всякими рюшечками. но всё равно всё сводится к банальному jump.

да ведь, в конце концов, return это оно же самое. возврат на стек, регистры восстановить, а потом — jump. но только почему никто не говорит, что return это плохо, и его надо выпилить? чё народ так непоследовательно-то мыслит?

Vladimir Moskva (fulc.ru) 2014

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

break и continue — скорее не goto с кучей проверок, а оператор, работающий единственно возможным способом. И их можно рассматривать не как goto, а как синтаксический сахар для if/else с дополнительными переменными.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для fulc.ru:

break и continue, к сожалению, работают только в циклах. Иногда надо сделать break из обычного if и цикла сверху нет.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для besisland.name:

break и continue — это очень плохо, и нужно стараться их никогда-никогда не использовать.

Кстати, напишите мне поиск указанного элемента массива без break.

Денис Попов (besisland.name) 2014

Вместо continue — просто оборачиваем в if.

Вместо break — добавляем условие цикла:

http://pastebin.com/ZM75jLzK

Я ещё return забыл — это тоже разновидность goto.

А вот try…catch — это goto технически, но не по смыслу. Их смысл — не в переходе в другое место программы, а в аварийной передаче управления.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для besisland.name:

Т. е. цикл каждую итерацию теперь проверяет ещё одно, совершенно ненужное условие? Плохое решение. Но, вы, смотрю, не специалист в программировании. is_null (функция) вместо === null (конструкция), count, вычисляемый в каждую итерацию цикла.

В общем, у вас простительные заблуждения.

zg (zg.livejournal.com) 2014

Комментарий для Евгения Степанищева:

Но, вы, смотрю, не специалист в программировании

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

Евгений Степанищев (bolknote.ru) 2014

Комментарий для zg.livejournal.com:

Да, пожалуй. Денис, прошу прощения.

Vladimir Moskva (fulc.ru) 2014

Комментарий для Евгения Степанищева:

Т. е. цикл каждую итерацию теперь проверяет ещё одно, совершенно ненужное условие?

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

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

Евгений Степанищев (bolknote.ru) 2014

Комментарий для fulc.ru:

А с использованием break он не будет проверять его каждый раз перед break?

Да, тут я промахнулся.

скорее всего, будет преждевременной оптимизацией.

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

gdriv.es/optiklab 2014

Добрый день.
    Работал пару лет назад в компании, в которой очень сильно следили за качеством кода, поэтому были огромные своды правил, как с точки зрения аккуратности кода (4 пробела вместо табов, отсутствие лишних пробелов в инструкциях или наличие таковых для удобочитаемости, и т. д.), так и с технической точки зрения (не использовать goto, continue, break, и т. д.).
    Однако, всё время в компании ходят споры о том, почему это нельзя делать: никто доводов толковых не приводил. Я сам об этом читал в книгах, вроде МакКоннел «Совершенный код», и т. д.
   Везде один довод: «запутанность кода». Однако при правильном разбиении кода на методы и нормальной архитектуре, по-моему, break и continue приводит только к большей понятности кода.
    И вот, читая интереснейший цикл статей «Что каждый программист должен знать о памяти» я нарвался на доступное объяснение чем плох любой переход (JUMP) с точки зрения производительности (ссыль http://rus-linux.net/lib.php?name=/MyLDP/hard/memory/memory-6-3.html%29:
« Код имеет то преимущество, что между переходами он линеен. В такие периоды процессор может эффективно делать предварительную загрузку памяти. Переходы нарушают эту идеальную картину из-за того что

  • цель перехода может быть определена не статически;
  • даже если она определена статически, загрузка памяти может занять длительное время, если эта цель отсутствует во всех кэшах.
         Эти проблемы создают задержки при выполнении кода с возможным существенным снижением производительности.»
Евгений Степанищев (bolknote.ru) 2014

Комментарий для http://gdriv.es/optiklab:

В случае интерпретируемых языков аргумент не принимается. Потому что при интерпретации происходит целая куча переходов, которые вы не видите. Кстати говоря, вызов любой функции/процедуры/метода — такой же переход.