LINUX.ORG.RU

Юзкейсы для goto

 ,


0

3

Пародия вот на это.

Вместо вот этого недоразумения

for (i = 0; i < len; i++) {
    if (some_func(arr[i]) == 0) {
        break;
    }
}

можно написать более изящно

for (i = 0; i < len; i++) {
    if (do_something(arr[i]) == 0) {
        goto OUT;
    }
}

OUT:
    do_something_else();


Последнее исправление: xenohunter (всего исправлений: 3)

Дело не в том, что goto плохой, а в том, что криворукие, как обычно, наломают дров. Проблемы из разряда глобальных переменных.

Bfgeshka ★★★★★
()
Ответ на: комментарий от xenohunter

Не очень хорошо, если в коде есть «что-то ненужное».

«ненужное» при преждевременном выходе из цикла (который должен происходить в редких исключительных случаях) и нужное при обычном выходе.

SystemD-hater
()

Неа, в данном случае break уместней. А вот если у тебя несколько точек выхода или нужно из вложенных циклов выйти, то да — goto самое место. Еще можно goto в макросах использовать, скажем:

#define TEST(x) do{if(!x) goto fail;}while(0)
sometype somefunction(args){
...
  char *a = malloc(100500);
  TEST(a);
  double g = do_some_stuff_with_a(a);
  TEST(g);
  ...
  return something; 
fail:
  free(a);
  free(b);
  free(g);
  ...
  return FAIL;
}

Eddy_Em ☆☆☆☆☆
()

С goto не более изящно. Изящно было бы так

do_something_with_arr();
...
do_something_else();
Причем, если тег c++ здесь не просто так, то можно было бы воспользоваться возможностью иметь локальные функции в виде лямбд:
void do_somethig(array_view<data> arr) {
  auto do_something_with_arr=[&] {
    for( auto & a : arr )
      if( some_func(a) == 0 )
        break;
  };
  do_something_with_arr();
  ...
  do_something_else();
}

eao197 ★★★★★
()
Ответ на: комментарий от xenohunter

Кто сказал, что этот код обязательно должен быть однострочный?

Deleted
()

Настоящая крутость goto проявляется при использовании gcc-шного расширения, которое позволяет брать указатели с этих меток и переходить по ним https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html . Это еще поддерживается clang-ом и ICC for linux

Этот прием используется в реализации парсера для строк в vfprintf от glibc https://github.com/bminor/glibc/blob/master/stdio-common/vfprintf.c

Правда компилятор падает если использовать особо наркоманские конструкции с этими метками для инициализации массива:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66178 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66123

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

SZT ★★★★★
()
Последнее исправление: SZT (всего исправлений: 1)
Ответ на: комментарий от i-rinat

Имхо, вопрос ТС происходит из-за того, что в C немного способов для структурирования кода. В C++ таких способов чуть больше, поэтому в C++ кроме выбора между break и goto есть и другие варианты.

Хотя не понятно, зачем ТС привлек тег c++.

eao197 ★★★★★
()
Ответ на: комментарий от i-rinat

Если прыгнешь из TEST(a), то в b будет мусор, а не NULL, на который ты расчитываешь.

Кмк, даже Эдди написал бы этот кусок как

#define TEST(x) do{if(!x) goto fail;}while(0)
sometype somefunction(args){
...
  char *a = NULL;
  char *b = NULL;
  double *g = NULL;

  a = malloc(100500);
  TEST(a);
  g = do_some_stuff_with_a(a);
  TEST(g);
  ...
  return something; 
fail:
  free(a);
  free(b);
  free(g);
  ...
  return FAIL;
}

Тем более, что сишникам привычно декларировать переменные «где-то в начале», а не «по месту использования»

Stil ★★★★★
()

Выйти из нескольких вложенных циклов.

newquestion
()
Ответ на: комментарий от Stil

Кмк, даже Эдди написал бы этот кусок как

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

Тем более, что сишникам привычно декларировать переменные «где-то в начале», а не «по месту использования»

Зависит от принятого в проекте стиля.

i-rinat ★★★★★
()
Ответ на: комментарий от Stil

Почему? Норм же.

Я ещё понимаю, зачем так выворачиваться, если планируется выход из вложенных циклов, return'ом. Но тут-то ради чего?

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Я ещё понимаю, зачем так выворачиваться, если планируется выход из вложенных циклов, return'ом. Но тут-то ради чего?

Так без вложенных циклов и goto вместо break'а не нужен.

Stil ★★★★★
()

goto в с используют чтоб память чистить.
К примеру

void *a = malloc(..)
bool result = false;
if (!do_som1()) 
 goto exit;
if (!do_some2())
 goto exit;
int i;
for (...) {
  if (!do_some3(i))
    goto exit;
}
result = true;

exit:
free(a);
return relult;

ymuv ★★★★
()
Последнее исправление: ymuv (всего исправлений: 2)

Для освобождения уже выделенных ресурсов, если в середине функции возникла какая-нибудь ошибка. Встречается чаще всего. Из середины вложенного цикла гораздо реже приходится выходить.

Vovka-Korovka ★★★★★
()
Ответ на: комментарий от ymuv

Кстати, если освобождать нужно только один ресурс и нет циклов, то некоторые извращенцы do{}while(0) используют и прерывают основной код через break.

Vovka-Korovka ★★★★★
()
Ответ на: комментарий от i-rinat

У меня обычно в самом начале функции все эти переменные NULL'у приравниваются, есличо.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Den_Zurin

Костыль для байтослесарей, в нормальных языках программирования есть try-catch-finally.

В нормальных языках есть функции.

anonymous
()

эээ... по-моему с break изящней :)

unt1tled ★★★★
()

кстати, в любимой жабке для этого есть именованные лупы

unt1tled ★★★★
()
Ответ на: комментарий от i-rinat

Если прыгнешь из TEST(a), то в b будет мусор, а не NULL

Даже хуже. Доступ к неинициализированной переменной — UB. А значит компилятор вправе считать, что при нём происходит что угодно. Например, переход обратно. То есть TEST в этом случае ничего не делает всегда. И при оптимизации правомерно молча выкинет все проверки TEST, кроме той, которая ниже инициализации всех трёх переменных.

monk ★★★★★
()
Ответ на: комментарий от Den_Zurin

Костыль для байтослесарей, в нормальных языках программирования есть try-catch-finally.

Это заведомо неправильное использование исключений :)

slackwarrior ★★★★★
()
Ответ на: комментарий от Vovka-Korovka

то некоторые извращенцы do{}while(0) используют и прерывают основной код через break.

Ещё можно

if (((a = malloc(100500)) || myerror("a"))
    && ((g = do_some_stuff_with_a(a)) || myerror("g"))
    && ((b = ...) || myerror("b")))
{
   ....
   тут вся программа
}
free(a);
free(b);
free(g);

monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)

В твоем примере можно обойтись без goto, выделив цикл в функцию и заменив goto на return. Но это может работать медленнее, если используется убогий компилятор, не умеющий инлайнить

annulen ★★★★★
()

Вы мне поясните такую вещь. Разве в С++ goto не ахтунг по причине того, что пямять под локальные переменные выделяется не вначале функций, а в месте объявления? Как компилятор разрулит ситуацию если между goto и меткой есть объявление локальной переменной с конструктором/деструктором?

Booster ★★
()
Ответ на: комментарий от Booster

Ок, компилятор отказывается такое переваривать.

Booster ★★
()

Короче, в следующий раз буду ссылку на вдохновивший пост ставить в начале. И добавлять sarcasm в теги.

xenohunter
() автор топика

вот этого недоразумения

написать более изящно

конкретно в твоём случае - break сделан специально для таких как ты

reprimand ★★★★★
()
Ответ на: комментарий от monk

Ну так, если обломится на первом строчке, то будет вызван free для b и g, для которых ресурсы еще не выделены. Либо я что-то не понял.

Vovka-Korovka ★★★★★
()
Ответ на: комментарий от Vovka-Korovka

Ну так, если обломится на первом строчке, то будет вызван free для b и g, для которых ресурсы еще не выделены. Либо я что-то не понял.

Инициализация указателей через NULL/nullptr/0 спасет отца русской демократии.

anonymous
()
Ответ на: комментарий от anonymous

Ну так это работает только в случае, если функция инициализации возвращает NULL как malloc. Если же вызывать функцию возвращающую код ошибки, то придется дополнительные переменные заводить для сохранения кода возврата. В любом случае, код с goto выглядел бы гораздо чище и понятнее.

Vovka-Korovka ★★★★★
()

Твоё «изящно», это унылое говно.

  1. При встрече слова break, я знаю, что выйду из цикла и могу сразу переместиться туда, при встрече goto LABEL, я сначала должен найти эту LABEL глазами или промотать код(где она там), чтобы посмотреть куда же я попаду, неудобно.
  2. На уровне ассемблера, все условия, брейки и цикли всё равно развернутся в те самые goto, так нахрена их ещё тут использовать? Высокоуровневые языки и писали для удобства разработки, а не скакания по линейке goto.
  3. Откуда же вы такие фанаты goto беретесь?
xterro ★★★★★
()
Последнее исправление: xterro (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.