Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > IT форум > Общие вопросы по программированию, компьютерный форум
Регистрация

Восстановить пароль

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы
Старый 15.09.2025, 19:33   #1
DeepFlake
Форумчанин
 
Регистрация: 16.05.2024
Сообщений: 229
По умолчанию [размышл.] "О бедном GOTO ..."

Нападки на оператор GOTO начал Дейкстра, и до сих пор некторые преподаватели программирования считают использование GOTO чем-то плохим.
Зачем же в используемых в настоящее время языках сохраняется лператор GOTO?

GOTO есть в Basic'е и Fortran'е - понятно почему, потому что они изначально были императивные и неструктурные.
Однако GOTO есть и во многих структурных языках: Free Pascal, Delphi, C, C++, Ada. Зачем?

Pascal и C появились одновременно как реакция на сложность и ненадёжность популярных в 60-е годы языков Algol, Fortran, PL/1. Из-за сложности и объёмности этих языков их трансляторы получались большими и не помещались целиков в память компьютера, и приходилось раздлять транслятор на части и загружать их последовательно. А в качестве устройства хранения информации тогда использовались магнитные ленты, которые работали медленно. Компиляция простейшей программы занимала 10 минут.

А языки C и Pascal были настолько просты, что их компилятор полностью умещался в оперативной памяти, и процесс программирования шёл быстрее. Но за простоту пришлось заплатить отсутствием автоматического управления памятью, встроенными средствами управления ресурсами (инициализатор-финализатор) и удобным выходом из нескольких циклов (break с меткой).

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

Рассмотрим на примере C как это надо делать (в Pascal'е аналогично).

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

Что бывает, если финализаторы не использовать: рассмотрим такой код.

Код:
int process_file(const char* path) {
    FILE* f = fopen(path, "r");
    if (!f) return ERROR_OPEN;
    
    char* buffer = malloc(1024);
    if (!buffer) {
        fclose(f);  // Правильно закрываем файл
        return ERROR_MEMORY;
    }
    
    if (fread(buffer, 1, 1024, f) < 10) {
        fclose(f);
        // Упс, забыли free(buffer)
        return ERROR_READ;
    }
    
    // Обрабатываем данные...
    free(buffer);
    fclose(f);
    return SUCCESS;
}
В этом коде несколько точек выхода из процедуры, и в одной из ветвей выполнения программы забыли освободить память перед выходом из процедуры. То есть происходит утечка памяти.

А вот так правильно надо написать эту процедуру с использованием финализатора.

Код:
int process_file(const char* path) 
{
    FILE* f = NULL;
    char* buffer = NULL;
    int result = SUCCESS;

    f = fopen(path, "r");
    if (!f)
    {
        result = ERROR_OPEN;
        goto FINISH;
    }
    
    buffer = malloc(1024);
    if (!buffer) 
    {
        result = ERROR_MEMORY;
        goto FINISH;
    }
    
    if (fread(buffer, 1, 1024, f) < 10) 
    {
        result = ERROR_READ;
        goto FINISH;
    }
    
    // Обрабатываем данные...


FINISH:
    if (buffer) free(buffer);
    if (f) fclose(f);
    return result;
}
Сейчас про break с меткой. Простой break выходит из текущего цикла, а что если надо выйти из нескольких вложенных циклов? Использовать флаги - это запутывает код. Вот как надо реализовать break с меткой.

Код:
for (i=0; i<19; i++)
{
    for (j=0; j<10; j++)
    {
        for (k=0; k<17; k++)
        {
            if (somecondition())
                goto LOOP_I_END;
        }
    }
LOOP_I_END:
}
Здесь происходит выход сразу из двух циклов (по k и j).

Язык C до сих пор широко используется, а Pascal уже не используется, но используются языки, совместимые с Pascal'ем: Free Pascal и Delphi. В них есть встренный в язык финализатор (try/finally), но break'а с меткой в них нет, так что GOTO в них всё ещё актуален.

В C++ финализатор можно организовать средствами объектно-ориентированного программирования, а break с меткой отсутствует, так что GOTO тоже нужен.

В Ada изначально был break с меткой, а в Ada-95 добавили объектно-ориенторованное программирование, и финализатор можно реализовать средствами ООП, но это неудобно в Ada. Так что GOTO в нём также приходится использовать.

Так что оператор GOTO всё ещё актуален во многих языках.

А теперь поговорим почему же Дейкстра так невзлюбил GOTO. Дело в том, что Дейкстра не был программистом, и программирование не было его увлечением в детстве. Дейкстра был физиком, и решил перейти в область компьютерного программирования из чисто конъюктурных соображений. Неудачник в физике, он попытался и к программированию применить своё видение науки. В частности, Дейкстра был уверен, что программирование - это часть математики, то есть наука. Но это в корне неверный взгляд, потому что программирование - это не наука, а инженерное дело. Дейкстра же разработал математическую теорию формального анализа компьютерных программ по их тексту, но эта теория работала только в тех случаях, когда в программах не было оператора GOTO (и выбросов исключений). Поэтому Дейкстра из своих личных интересов (для продвижения своей теории) нападал и дискредитировал языки, в которых используется GOTO, и на сам оператор GOTO.
DeepFlake вне форума Ответить с цитированием
Старый 16.09.2025, 07:28   #2
Алексей1153
фрилансер, препод.
Участник клуба
 
Регистрация: 11.10.2019
Сообщений: 1,062
По умолчанию

Цитата:
Сообщение от DeepFlake Посмотреть сообщение
Рассмотрим на примере C как это надо делать
делается функция-обёртка, и никаких goto не нужно

Цитата:
Сообщение от DeepFlake Посмотреть сообщение
Зачем же в используемых в настоящее время языках сохраняется лператор GOTO?
что касается C++ - там сохраняется просто для поддержки древнего кода

Цитата:
Сообщение от DeepFlake Посмотреть сообщение
В C++ финализатор можно организовать
при помощи IIFE . То есть, безымянная лямбда с немедленным вызовом
Но и это очень редко требуется

Лучше, конечно, RAII использовать.

И ещё goto ударяет по возможностям оптимизатора

----------
резюме:
goto в C++ нынче не нужен (не из-за религии, а просто сам по себе), ударяет по оптимизации и провоцирует на запутанный код

Последний раз редактировалось Алексей1153; 16.09.2025 в 07:30.
Алексей1153 вне форума Ответить с цитированием
Старый 16.09.2025, 13:42   #3
Arigato
Высокая репутация
СуперМодератор
 
Аватар для Arigato
 
Регистрация: 27.07.2008
Сообщений: 15,938
По умолчанию

Цитата:
Сообщение от DeepFlake Посмотреть сообщение
Дело в том, что Дейкстра не был программистом, и программирование не было его увлечением в детстве.
А у многих детей 1930-40-хх годов программирование было увлечением?
Arigato вне форума Ответить с цитированием
Старый 16.09.2025, 18:22   #4
evg_m
Старожил
 
Регистрация: 20.04.2008
Сообщений: 5,545
По умолчанию

GOTO бывает хороша только в момент ее написания.
А вот потом...
программы имеют обыкновения модернизироваться(исправляться) и не обязательно автором первоначального кода.

Цитата:
А вот так правильно надо написать эту процедуру с использованием финализатора.
а теперь (через неделю/месяц когда мы уже забыли зачем мы ввели FINISH) добавим еще метку FINISH2 ниже FINISH (и GOTO к ней) .
и что в итоге ?

Любое GOTO провоцирует неверный код(алгоритм) в момент ИСПРАВЛЕНИЯ(модификации) не первоначального написания.
программа — запись алгоритма на языке понятном транслятору

Последний раз редактировалось evg_m; 16.09.2025 в 18:33.
evg_m вне форума Ответить с цитированием
Старый 16.09.2025, 18:38   #5
Arigato
Высокая репутация
СуперМодератор
 
Аватар для Arigato
 
Регистрация: 27.07.2008
Сообщений: 15,938
По умолчанию

Цитата:
Сообщение от DeepFlake Посмотреть сообщение
Дейкстра был физиком, и решил перейти в область компьютерного программирования из чисто конъюктурных соображений. Неудачник в физике, он попытался и к программированию применить своё видение науки.
Я тут глянул биография Дейкстра и что-то факты не сходятся.

По окончании школы поступил на факультет теоретической физики Лейденского университета.

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

В 1951 году увлёкся программированием, поступил на трёхнедельные компьютерные курсы в Кембридже, с 1952 года работал программистом в Математическом центре Амстердама под руководством профессора Адриана ван Вейнгаардена

То есть в 21 год он таки увлекся программированием и даже закончил компьютерные курсы (внимание, это в начале 1950-х). А с 22 лет уже работал программистом.

Уже в 1952 году принял решение окончательно специализироваться на программировании, но всё же окончил курс теоретической физики.

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

...
Arigato вне форума Ответить с цитированием
Старый 16.09.2025, 18:52   #6
DeepFlake
Форумчанин
 
Регистрация: 16.05.2024
Сообщений: 229
По умолчанию

2 Arigato: А в чём не сходятся факты? Физик? - Да, физик. А у ван Вейнгаардена он не работал, а был аспирантом.
DeepFlake вне форума Ответить с цитированием
Старый 16.09.2025, 18:58   #7
DeepFlake
Форумчанин
 
Регистрация: 16.05.2024
Сообщений: 229
По умолчанию

>безымянная лямбда с немедленным вызовом

Финализатор - это же отложенное выполнение кода (до окончания блока), так что IIFE - не пойдёт.

>делается функция-обёртка, и никаких goto не нужно

А что проще - cleanup-код во вспомогательной функции или непоседственно в основной функции? Мне кажется в основной.

>goto ударяет по возможностям оптимизатора

наверное больше то проблема оптимизаторов а не goto
DeepFlake вне форума Ответить с цитированием
Старый 16.09.2025, 21:45   #8
Arigato
Высокая репутация
СуперМодератор
 
Аватар для Arigato
 
Регистрация: 27.07.2008
Сообщений: 15,938
По умолчанию

Цитата:
Сообщение от DeepFlake Посмотреть сообщение
А в чём не сходятся факты? Физик? - Да, физик.
А кем ему надо было быть? Ну математиком мог бы быть. Скажете, опять не программист. Так не было в то время такой специальности. О ней даже не слышали.

Вот в той же статье есть такая занимательная история: "В 1957 году женился, по собственным воспоминаниям, в графе «профессия» анкеты, которую положено заполнять при бракосочетании, написал «программист» — и его заставили переписывать документы, заявив, что такой профессии не существует, в результате пришлось указать «физик-теоретик»".

А вы теперь говорите, он же физик-теоретик, сам же признался

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

Последний раз редактировалось Arigato; 16.09.2025 в 21:47.
Arigato вне форума Ответить с цитированием
Старый 18.09.2025, 05:56   #9
сфинкс
Участник клуба
 
Аватар для сфинкс
 
Регистрация: 17.06.2012
Сообщений: 1,028
По умолчанию

Замена GOTO qb64 на флаг Python в моей теме из подписи
про Случайные и массив в задаче Ребус Букв
https://www.programmersforum.ru/show...5&postcount=27
https://www.programmersforum.ru/show...95#post1860195

qb64
Код:
        For ii=1 To NN - 1
            For jj=ii + 1 To NN
            If a(ii)=a(jj) Then GoTo 55
        Next: Next 
...
55 Next: Next: Next: Next: Next: Next: Next: Next
End
Python
Код:
        for ii in range (1,nn-1):
          for jj in range (ii+1,nn):
             if x[ii] == x[jj]:
                ff = 1

        if ff == 0: 
...
        ff=0
Случайные и Массивы https://programmersforum.ru/showthread.php?t=344371 Учим C# & basic & excel & python https://programmersforum.ru/showthre...=327446&page=5 ничего нерекомендую

Последний раз редактировалось сфинкс; 18.09.2025 в 05:59.
сфинкс вне форума Ответить с цитированием
Старый 18.09.2025, 15:08   #10
DeepFlake
Форумчанин
 
Регистрация: 16.05.2024
Сообщений: 229
По умолчанию

А, в C# оказывается тоже есть goto, ну вот видите ...
Дисциплина нужна, конечно, когда используют goto, а её часто не хватает
DeepFlake вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Необходимо написать "таймер", который будет запускаться при нажатии кнопки "Start", приостанавливаться на "Pause", и сбрасываться на "Reset" billiejean78 JavaScript, Ajax 1 03.09.2021 08:58
Убрать папки "Pictures", "Music", "Видео", "Downloads" из "МОЙ КОМПЬЮТЕР" Бахтиёр1916 Windows 1 05.04.2017 12:53
Нужно пояснить/прокомментировать код программы, или коды функций "Добавить" "Удалить" "Обновить(редактировать" "Поиск" "Период") ZIRASS PHP 4 15.06.2016 14:23
Для заданной строки определить все входящие в неё символ. Например: строка "abccbbabbac" состоит из символов "a", "b" и "c" Sandakan01 Помощь студентам 1 24.02.2016 03:20