Выгодно ли когда-либо использовать «goto» в языке, который поддерживает циклы и функции? Если так, то почему? - PullRequest
189 голосов
/ 23 августа 2008

У меня давно сложилось впечатление, что goto никогда не следует использовать, если это возможно. Просматривая libavcodec (который написан на C) на днях, я заметил многократное его использование. Всегда ли выгодно использовать goto в языке, который поддерживает циклы и функции? Если так, то почему?

Ответы [ 24 ]

1 голос
/ 23 августа 2008

Проблема с «goto» и наиболее важным аргументом движения «gotoless-программирование» состоит в том, что если вы используете его слишком часто, ваш код, хотя он может вести себя правильно, становится нечитаемым, не поддерживаемым, недоступным для чтения и т. Д. В 99,99% случаев «goto» приводит к коду спагетти. Лично я не могу придумать вескую причину того, почему я бы использовал «goto».

1 голос
/ 25 августа 2008

Эдсгер Дейкстра (Edsger Dijkstra), ученый-компьютерщик, внесший большой вклад в этой области, также был известен своей критикой использования GoTo. Есть короткая статья о его аргументе в Википедии .

1 голос
/ 14 октября 2016

Некоторые говорят, что нет причины для перехода в C ++. Некоторые говорят, что в 99% случаев есть лучшие альтернативы. Это не рассуждения, а просто иррациональные впечатления. Вот хороший пример, где goto приводит к хорошему коду, что-то вроде расширенного цикла do-while:

int i;

PROMPT_INSERT_NUMBER:
  std::cout << "insert number: ";
  std::cin >> i;
  if(std::cin.fail()) {
    std::cin.clear();
    std::cin.ignore(1000,'\n');
    goto PROMPT_INSERT_NUMBER;          
  }

std::cout << "your number is " << i;

Сравните это с кодом goto-free:

int i;

bool loop;
do {
  loop = false;
  std::cout << "insert number: ";
  std::cin >> i;
  if(std::cin.fail()) {
    std::cin.clear();
    std::cin.ignore(1000,'\n');
    loop = true;          
  }
} while(loop);

std::cout << "your number is " << i;

Я вижу эти различия:

  • требуется вложенный {} блок (хотя do {...} while выглядит более знакомым)
  • дополнительная loop требуется переменная, используется в четырех местах
  • требуется больше времени, чтобы прочитать и понять работу с loop
  • loop не содержит никаких данных, он просто контролирует поток выполнения, который менее понятен, чем простая метка

Есть еще один пример

void sort(int* array, int length) {
SORT:
  for(int i=0; i<length-1; ++i) if(array[i]>array[i+1]) {
    swap(data[i], data[i+1]);
    goto SORT; // it is very easy to understand this code, right?
  }
}

Теперь давайте избавимся от "злого" перехода:

void sort(int* array, int length) {
  bool seemslegit;
  do {
    seemslegit = true;
    for(int i=0; i<length-1; ++i) if(array[i]>array[i+1]) {
      swap(data[i], data[i+1]);
      seemslegit = false;
    }
  } while(!seemslegit);
}

Вы видите, что это тот же тип использования goto, это хорошо структурированный паттерн, и он не продвигает goto так, как многие продвигают, как единственный рекомендуемый способ. Конечно, вы хотите избежать «умного» кода, подобного этому:

void sort(int* array, int length) {
  for(int i=0; i<length-1; ++i) if(array[i]>array[i+1]) {
    swap(data[i], data[i+1]);
    i = -1; // it works, but WTF on the first glance
  }
}

Дело в том, что goto можно легко использовать неправильно, но само goto не виновато. Обратите внимание, что метка имеет область действия функции в C ++, поэтому она не загрязняет глобальную область видимости, как в чистой сборке, в которой перекрывающиеся циклы имеют свое место и очень распространены - как в следующем коде для 8051, где 7-сегментное отображение подключен к P1. Программа зацикливает молниеносный сегмент вокруг:

; P1 states loops
; 11111110 <-
; 11111101  |
; 11111011  |
; 11110111  |
; 11101111  |
; 11011111  |
; |_________|

init_roll_state:
    MOV P1,#11111110b
    ACALL delay
next_roll_state:
    MOV A,P1
    RL A
    MOV P1,A
    ACALL delay
    JNB P1.5, init_roll_state
    SJMP next_roll_state

Есть еще одно преимущество: goto может служить именованными циклами, условиями и другими потоками:

if(valid) {
  do { // while(loop)

// more than one page of code here
// so it is better to comment the meaning
// of the corresponding curly bracket

  } while(loop);
} // if(valid)

Или вы можете использовать эквивалентное goto с отступом, поэтому вам не нужно комментировать, если вы правильно выберете название метки:

if(!valid) goto NOTVALID;
  LOOPBACK:

// more than one page of code here

  if(loop) goto LOOPBACK;
NOTVALID:;
1 голос
/ 23 августа 2008

В Perl использование метки для «перехода» из цикла - с использованием «последней» инструкции, которая похожа на break.

Это позволяет лучше контролировать вложенные циклы.

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

...