Примеры хороших переходов на C или C++ [закрыто]

StackOverflow https://stackoverflow.com/questions/245742

  •  05-07-2019
  •  | 
  •  

Вопрос

В этой теме мы рассмотрим примеры удачного использования goto на С или С++.Это вдохновлено Ответ за который люди проголосовали, потому что подумали, что я шучу.

Резюме (этикетка изменена с оригинальной, чтобы сделать намерения еще более понятными):

infinite_loop:

    // code goes here

goto infinite_loop;

Почему это лучше альтернатив:

  • Это специфично. goto это языковая конструкция, которая вызывает безусловную ветвь.Альтернативы зависят от использования структур, поддерживающих условные ветви, с дегенеративным условием, всегда подходящим.
  • Этикетка документирует намерение без дополнительных комментариев.
  • Читателю не нужно сканировать промежуточный код на раннем этапе breakS (хотя беспринципированный хакер по -прежнему возможноcontinue с ранним goto).

Правила:

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

Посмотрим, сможем ли мы поговорить об этом как взрослые.

Редактировать

Кажется, этот вопрос уже решен.Это дало несколько высококачественных ответов.Спасибо всем, особенно тем, кто серьезно относился к моему маленькому петлю.Большинство скептиков были обеспокоены отсутствием схватки блоков.Как указал @quinmars в комментарии, вы всегда можете положить брекеты вокруг тела петли.Замечу попутно, что for(;;) и while(true) Не дайте вам брекеты бесплатно (и их пропущение может вызвать раздражающие ошибки).В любом случае, я больше не буду тратить вашу мозговую силу на эту мелочь - я могу жить с безвредным и идиоматическим for(;;) и while(true) (так же хорошо, если я хочу сохранить свою работу).

Учитывая другие ответы, я вижу, что многие люди рассматривают goto Как то, что вам всегда нужно переписать по -другому.Конечно, вы можете избежать goto Представляя петлю, дополнительный флаг, стопку вложенного ifs или что-то в этом роде, но почему бы не подумать о том, goto Возможно, лучший инструмент для работы?Другими словами, сколько уродств готовы пережить люди, чтобы не использовать встроенную языковую функцию по прямому назначению?Я считаю, что даже добавление флага слишком высока цена, чтобы заплатить.Мне нравится мои переменные, чтобы представлять вещи в проблемных или решениях решения.«Исключительно для того, чтобы избежать goto' не подходит.

Я приму первый ответ, который дает шаблон C для перехода к блоку очистки.ИМХО, это самый веский аргумент в пользу goto Из всех опубликованных ответов, конечно, если вы измеряете это по конфортам, через которые должен пройти ненавистник, чтобы избежать этого.

Это было полезно?

Решение

Вот один трюк, который я слышал от людей.Хотя я никогда не видел его в дикой природе.И это применимо только к C, потому что в C++ есть RAII, позволяющий делать это более идиоматично.

void foo()
{
    if (!doA())
        goto exit;
    if (!doB())
        goto cleanupA;
    if (!doC())
        goto cleanupB;

    /* everything has succeeded */
    return;

cleanupB:
    undoB();
cleanupA:
    undoA();
exit:
    return;
}

Другие советы

Классическая необходимость GOTO в C заключается в следующем:

for ...
  for ...
    if(breakout_condition) 
      goto final;

final:

Не существует простого способа выйти из вложенных циклов без перехода.

Вот мой неглупый пример (от Стивенса APITUE) для системных вызовов Unix, которые могут быть прерваны сигналом.

restart:
    if (system_call() == -1) {
        if (errno == EINTR) goto restart;

        // handle real errors
    }

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

Если устройству Даффа переход не нужен, то и вам не нужен!;)

void dsend(int count) {
    int n;
    if (!count) return;
    n = (count + 7) / 8;
    switch (count % 8) {
      case 0: do { puts("case 0");
      case 7:      puts("case 7");
      case 6:      puts("case 6");
      case 5:      puts("case 5");
      case 4:      puts("case 4");
      case 3:      puts("case 3");
      case 2:      puts("case 2");
      case 1:      puts("case 1");
                 } while (--n > 0);
    }
}

код выше из Википедии вход.

Кнут написал статью «Структурное программирование с операторами GOTO», вы можете получить ее, например.от здесь.Там вы найдете множество примеров.

Очень распространен.

do_stuff(thingy) {
    lock(thingy);

    foo;
    if (foo failed) {
        status = -EFOO;
        goto OUT;
    }

    bar;
    if (bar failed) {
        status = -EBAR;
        goto OUT;
    }

    do_stuff_to(thingy);

OUT:
    unlock(thingy);
    return status;
}

Единственный случай, который я когда-либо использовал goto предназначен для прыжков вперед, обычно из блоков и никогда в блоки.Это позволяет избежать злоупотреблений do{}while(0) и другие конструкции, которые увеличивают вложенность, сохраняя при этом читаемый структурированный код.

В целом я ничего не имею против gotos, но могу придумать несколько причин, по которым вы не захотите использовать их для цикла, как вы упомянули:

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

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

if (!openDataFile())
  goto quit;

if (!getDataFromFile())
  goto closeFileAndQuit;

if (!allocateSomeResources)
  goto freeResourcesAndQuit;

// Do more work here....

freeResourcesAndQuit:
   // free resources
closeFileAndQuit:
   // close file
quit:
   // quit!

@fizzer.myopenid.com:ваш опубликованный фрагмент кода эквивалентен следующему:

    while (system_call() == -1)
    {
        if (errno != EINTR)
        {
            // handle real errors

            break;
        }
    }

Я определенно предпочитаю эту форму.

Хотя со временем я возненавидел этот шаблон, он прочно укоренился в COM-программировании.

#define IfFailGo(x) {hr = (x); if (FAILED(hr)) goto Error}
...
HRESULT SomeMethod(IFoo* pFoo) {
  HRESULT hr = S_OK;
  IfFailGo( pFoo->PerformAction() );
  IfFailGo( pFoo->SomeOtherAction() );
Error:
  return hr;
}

Вот пример хорошего перехода:

// No Code

Я видел правильное использование goto, но ситуации обычно ужасны.И только тогда, когда использование goto сам по себе гораздо менее хуже оригинала.@Johnathon Holland, проблема в том, что твоя версия менее ясна.люди, кажется, боятся локальных переменных:

void foo()
{
    bool doAsuccess = doA();
    bool doBsuccess = doAsuccess && doB();
    bool doCsuccess = doBsuccess && doC();

    if (!doCsuccess)
    {
        if (doBsuccess)
            undoB();
        if (doAsuccess)
            undoA();
    }
}

И я предпочитаю такие циклы, но некоторые люди предпочитают while(true).

for (;;)
{
    //code goes here
}

Меня беспокоит то, что вы теряете область видимости блока;любые локальные переменные, объявленные между переходами, остаются в силе, даже если цикл когда-либо прерывается.(Возможно, вы предполагаете, что цикл выполняется вечно;Я не думаю, что это то, что задавал первоначальный автор вопроса.)

Проблема с областью действия больше связана с C++, поскольку некоторые объекты могут зависеть от того, вызывается ли их dtor в подходящее время.

Для меня лучшая причина использовать goto — это многоэтапный процесс инициализации, где очень важно, чтобы все инициализации были отменены в случае сбоя одной из них, а-ля:

if(!foo_init())
  goto bye;

if(!bar_init())
  goto foo_bye;

if(!xyzzy_init())
  goto bar_bye;

return TRUE;

bar_bye:
 bar_terminate();

foo_bye:
  foo_terminate();

bye:
  return FALSE;

Я сам не использую goto, однако однажды работал с человеком, который использовал их в определенных случаях.Если я правильно помню, его аргументация заключалась в проблемах с производительностью — у него также были особые правила для как.Всегда в одной и той же функции, и метка всегда находилась НИЖЕ оператора перехода.

#include <stdio.h>
#include <string.h>

int main()
{
    char name[64];
    char url[80]; /*The final url name with http://www..com*/
    char *pName;
    int x;

    pName = name;

    INPUT:
    printf("\nWrite the name of a web page (Without www, http, .com) ");
    gets(name);

    for(x=0;x<=(strlen(name));x++)
        if(*(pName+0) == '\0' || *(pName+x) == ' ')
        {
            printf("Name blank or with spaces!");
            getch();
            system("cls");
            goto INPUT;
        }

    strcpy(url,"http://www.");
    strcat(url,name);
    strcat(url,".com");

    printf("%s",url);
    return(0);
}

@Грег:

Почему бы не сделать ваш пример следующим образом:

void foo()
{
    if (doA())
    {    
        if (doB())
        {
          if (!doC())
          {
             UndoA();
             UndoB();
          }
        }
        else
        {
          UndoA();
        }
    }
    return;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top