Будет ли использоваться переменная утечки goto?

94

Верно ли, что gotoперескакивает по битам кода без вызова деструкторов и прочего?

например

void f() {
   int x = 0;
   goto lol;
}

int main() {
   f();
lol:
   return 0;
}

Не xбудет утечки?

Гонки легкости на орбите
источник
По теме: stackoverflow.com/questions/1258201/… (но я хотел сделать это с нуля, чисто!)
Гонки легкости на орбите
15
Что "Won't x be leaked"значит? Тип x- это встроенный тип данных. Почему бы тебе не выбрать лучший пример?
Nawaz
2
@Nawaz: Пример и так идеален. Почти каждый раз, когда я говорю с кем-нибудь goto, они думают, что даже автоматические переменные продолжительности хранения каким-то образом «просочились». То, что мы с вами знаем иное, совершенно не относится к делу.
Гонки легкости на орбите,
1
@David: Я согласен с тем, что этот вопрос имеет гораздо больше смысла, когда переменная имеет нетривиальный деструктор ... и я просмотрел ответ Томалака и нашел такой пример. Кроме того, в то время как intне может течь, это может быть утечка . Например: void f(void) { new int(5); }утечка int.
Бен Фойгт,
Почему бы не изменить вопрос на что-то вроде «В приведенном примере будет ли путь выполнения кода передаваться от f () к main () без очистки стека и других функций возврата из функции? Будет ли иметь значение, если деструктор должен быть называется? Это то же самое в C? " Сможет ли это поддержать цель вопроса и избежать возможных неправильных представлений?
Джек В.

Ответы:

210

Предупреждение: Этот ответ относится к C ++ только ; в C.


Не xбудет утечки?

Нет, абсолютно нет.

Это миф, что gotoнекоторая низкоуровневая конструкция позволяет вам переопределить встроенные в C ++ механизмы определения области видимости. (Во всяком случае, это longjmpможет быть подвержено этому.)

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


1. Объем ярлыка

Вы не можете перепрыгивать через функции:

void f() {
   int x = 0;
   goto lol;
}

int main() {
   f();
lol:
   return 0;
}

// error: label 'lol' used but not defined

[n3290: 6.1/1]:[..] Объем метки - это функция, в которой она появляется. [..]


2. Инициализация объекта

Вы не можете перепрыгнуть через инициализацию объекта:

int main() {
   goto lol;
   int x = 0;
lol:
   return 0;
}

// error: jump to label ‘lol’
// error:   from here
// error:   crosses initialization of ‘int x’

Если вы вернетесь назад через инициализацию объекта, то предыдущий «экземпляр» объекта будет уничтожен :

struct T {
   T() { cout << "*T"; }
  ~T() { cout << "~T"; }
};

int main() {
   int x = 0;

  lol:
   T t;
   if (x++ < 5)
     goto lol;
}

// Output: *T~T*T~T*T~T*T~T*T~T*T~T

[n3290: 6.6/2]:[..] Передача из цикла, из блока или обратно через инициализированную переменную с автоматической продолжительностью хранения включает в себя уничтожение объектов с автоматической продолжительностью хранения, которые находятся в области видимости в точке передачи, но не в точке, переданной в . [..]

Вы не можете перейти в область видимости объекта, даже если он не инициализирован явно:

int main() {
   goto lol;
   {
      std::string x;
lol:
      x = "";
   }
}

// error: jump to label ‘lol’
// error:   from here
// error:   crosses initialization of ‘std::string x’

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

int main() {
   goto lol;
   {
      int x;
lol:
      x = 0;
   }
}

// OK

[n3290: 6.7/3]:Можно передать в блок, но не в обход объявлений с инициализацией. Программа, которая перескакивает из точки, где переменная с автоматической продолжительностью хранения не находится в области видимости, к точке, где она находится в области видимости, плохо сформирована, если только переменная не имеет скалярный тип, тип класса с тривиальным конструктором по умолчанию и тривиальный деструктор, a Версия одного из этих типов с квалификацией cv или массив одного из предыдущих типов объявляется без инициализатора. [..]


3. Прыжки подчиняются другим объектам.

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

struct T {
   T() { cout << "*T"; }
  ~T() { cout << "~T"; }
};

int main() {
   {
      T t;
      goto lol;
   }

lol:
   return 0;
}

// *T~T

[n3290: 6.6/2]:При выходе из области видимости (независимо от ее выполнения) объекты с автоматической продолжительностью хранения (3.7.3), которые были созданы в этой области, уничтожаются в порядке, обратном их построению. [..]


Вывод

Вышеупомянутые механизмы гарантируют, что gotoвы не сломаете язык.

Конечно, это не означает автоматически , что вы «должны» использовать gotoдля любой данной проблемы, но это действительно означает , что это не так «зла» , как общий миф заставляет людей верить.

Гонки легкости на орбите
источник
8
Вы можете заметить, что C не предотвращает всех этих опасных вещей.
Даниэль,
13
@ Дэниел: Вопрос и ответ очень конкретно касаются C ++, но справедливо. Может быть, у нас есть еще один FAQ, развеивающий миф о том, что C и C ++ - это одно и то же;)
Lightness Races in Orbit
3
@Tomalak: Я не думаю, что мы здесь не согласны. Многие ответы на SO явно где-то задокументированы. Я просто указал на то, что программисту на C может показаться соблазнительным увидеть этот ответ и предположить, что если он работает на C ++, он должен работать аналогично и на C.
Даниэль
2
Вы также можете добавить, что все эти переходы через инициализацию одинаковы для меток case.
PlasmaHH
12
Вау, я только что предположил, что семантика C ++ нарушена для goto, но они на удивление разумны! Отличный ответ.
Джозеф Гарвин,