Как указывалось в других ответах, в простом примере это мало что дает;его преимущества становятся более очевидными, когда есть несколько ресурсов для добычи и несколько ошибок.Этот псевдокод демонстрирует это:
boolean allocate_resources (ObjectA **a, ObjectB **b, ObjectC **c)
{
*a = allocate_a();
if (*a == NULL) {
LOG_ERROR("failed to allocate A");
goto fail0;
}
*b = allocate_b();
if (*b == NULL) {
LOG_ERROR("failed to allocate B");
goto fail1;
}
*c = allocate_c();
if (*c == NULL) {
LOG_ERROR("failed to allocate C");
goto fail2;
}
return true;
fail2:
release_b(*b);
fail1:
release_a(*a);
fail0:
return false;
}
Обратите внимание, что вышеуказанная функция работает атомарно, как видно снаружи - при возврате либо все ресурсы распределяются, либо ни один из них, избегая утечек.
Сравните это с одной из возможных версий, в которой вместо goto используется вложение:
// Bad style. Don't do this.
boolean allocate_resources (ObjectA **a, ObjectB **b, ObjectC **c)
{
*a = allocate_a();
if (*a == NULL) {
LOG_ERROR("failed to allocate A");
} else {
*b = allocate_b();
if (*b == NULL) {
LOG_ERROR("failed to allocate B");
} else {
*c = allocate_c();
if (*c == NULL) {
LOG_ERROR("failed to allocate C");
} else {
return true;
}
release_b(*b);
}
release_a(*a);
}
return false;
}
Эта версия намного сложнее для чтения.В версии goto на первый взгляд очевидно, каким будет основной путь выполнения;здесь это не так.Вложенность затрудняет чтение кода, а также тратит впустую горизонтальное пространство.
Надеюсь, я не испортил его, и он функционально эквивалентен примеру goto;Я предлагаю вам попытаться доказать, что это эквивалентно, и лично убедиться, насколько сложнее работать с ним.