Примеры хорошего gotos в C или [закрытом] C++

79
задан Community 23 May 2017 в 12:02
поделиться

15 ответов

Heres один прием я услышал о людях, использующих. Я никогда не видел его в дикой природе все же. И это только относится к 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;
}
87
ответ дан Muhammad Annaqeeb 24 November 2019 в 10:02
поделиться

@Greg:

, Почему бы не Ваш пример как это:

void foo()
{
    if (doA())
    {    
        if (doB())
        {
          if (!doC())
          {
             UndoA();
             UndoB();
          }
        }
        else
        {
          UndoA();
        }
    }
    return;
}
-1
ответ дан FlySwat 24 November 2019 в 10:02
поделиться

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

0
ответ дан user25306 24 November 2019 в 10:02
поделиться

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

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

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

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;
0
ответ дан Jim Nelson 24 November 2019 в 10:02
поделиться

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

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
}
1
ответ дан Charles Beattie 24 November 2019 в 10:02
поделиться

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

// No Code
5
ответ дан FlySwat 24 November 2019 в 10:02
поделиться

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

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

            break;
        }
    }

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

7
ответ дан Mitch Wheat 24 November 2019 в 10:02
поделиться

Даже при том, что я возненавидел этот шаблон со временем, он внушен в программирование 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;
}
6
ответ дан Patrick 24 November 2019 в 10:02
поделиться

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

  • Это не ограничивает объем следовательно никакие временные переменные, которые Вы используете, внутри не будет освобожден до позже.
  • Это не ограничивает объем следовательно, это могло привести к ошибкам.
  • Это не ограничивает объем следовательно, Вы не можете снова использовать те же имена переменной позже в будущем коде в том же объеме.
  • Это не ограничивает объем следовательно, у Вас есть шанс перескакивания через объявление переменной.
  • Люди не приучены к нему, и это сделает Ваш код тяжелее для чтения.
  • Вложенные циклы этого типа могут привести к запутанному коду, normals циклы не приведет к запутанному коду.
12
ответ дан Mitch Wheat 24 November 2019 в 10:02
поделиться

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

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) и другие конструкции, которые увеличивают вложение при тихом поддержании читаемого, структурированного кода.

12
ответ дан ephemient 24 November 2019 в 10:02
поделиться

Knuth написал работу "Структурное программирование с Операторами перехода", можно получить его, например, от здесь . Вы найдете много примеров там.

14
ответ дан zvrba 24 November 2019 в 10:02
поделиться

Если устройству Вареного пудинга не нужен goto, то ни если Вы!;)

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);
    }
}

код выше из Википедии запись .

14
ответ дан Mitch Wheat 24 November 2019 в 10:02
поделиться

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

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

final:

нет никакого простого способа убежать из вложенных циклов без goto.

85
ответ дан Paul Nathan 24 November 2019 в 10:02
поделиться

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

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

        // handle real errors
    }

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

32
ответ дан Mitch Wheat 24 November 2019 в 10:02
поделиться

Одно хорошее место для использования goto находится в процедуре, которая может прерваться в нескольких точках, каждая из которых требует различных уровней очистки. Gotophobes может всегда заменять 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!
7
ответ дан Adam Liss 24 November 2019 в 10:02
поделиться