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

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

e.g.

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

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

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


person Lightness Races in Orbit    schedule 07.09.2011    source источник
comment
Связано: stackoverflow.com/questions/1258201/ (но хотелось сделать с нуля, чисто!)   -  person Lightness Races in Orbit    schedule 07.09.2011
comment
Что значит "Won't x be leaked"? Тип x - это встроенный тип данных. Почему бы тебе не выбрать лучший пример?   -  person Nawaz    schedule 07.09.2011
comment
@Nawaz: Пример и так идеален. Почти каждый раз, когда я говорю с кем-нибудь о goto, они думают, что даже автоматические переменные продолжительности хранения каким-то образом просочились. То, что мы с вами знаем иное, совершенно не имеет значения.   -  person Lightness Races in Orbit    schedule 07.09.2011
comment
@David: Я согласен с тем, что этот вопрос имеет гораздо больше смысла, когда переменная имеет нетривиальный деструктор ... и я просмотрел ответ Томалака и нашел такой пример. Более того, хотя int не может протекать, он может протекать. Например: void f(void) { new int(5); } утекает int.   -  person Ben Voigt    schedule 07.09.2011
comment
Почему бы не изменить вопрос на что-то подобное. В приведенном примере будет ли путь выполнения кода передаваться от f () к main () без очистки стека и других функций возврата из функции? Будет ли иметь значение вызов деструктора? То же самое и в C? Будет ли это поддерживать цель вопроса, избегая при этом возможных неправильных представлений?   -  person Jack V.    schedule 14.09.2011
comment
@Jack: Ага, я согласен с этим (хотя язык там может иметь высокие требования к знаниям)   -  person Lightness Races in Orbit    schedule 14.09.2011


Ответы (1)


Предупреждение: этот ответ относится только к 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 для решения той или иной проблемы, но это действительно означает, что это не так уж плохо, как заставляет людей верить распространенный миф.

person Lightness Races in Orbit    schedule 07.09.2011
comment
@Ben: Не [n3290: 18.10/4]:. Сигнатура функции longjmp(jmp_buf jbuf, int val) имеет более ограниченное поведение в этом международном стандарте. Пара вызовов _3 _ / _ 4_ имеет неопределенное поведение, если замена setjmp и longjmp на catch и throw вызовет любые нетривиальные деструкторы для любых автоматических объектов. сломать это? - person Lightness Races in Orbit; 07.09.2011
comment
Вы можете заметить, что C не предотвращает всех этих опасных вещей. - person Daniel; 07.09.2011
comment
@Daniel: Вопрос и ответ очень конкретно касаются C ++, но справедливо. Может быть, у нас есть еще один FAQ, развеивающий миф о том, что C и C ++ - это одно и то же;) - person Lightness Races in Orbit; 07.09.2011
comment
Например, goto lol; int x = 5; lol: printf("%d", x); дает warning: ‘x’ is used uninitialized in this function - person Daniel; 07.09.2011
comment
@Tomalak: Вы правы, но я думаю, будет справедливо отметить, что что-то вроде goto, существовавшее в C до появления C ++, на самом деле не работает точно так же в C ++. - person Daniel; 07.09.2011
comment
@Daniel: Для записи, это различие с C явно указано в разделе совместимости [n3290: C.1.5]. - person Lightness Races in Orbit; 07.09.2011
comment
@Tomalak: Я не думаю, что мы здесь не согласны. Многие ответы на SO явно где-то задокументированы. Я просто отметил, что для программиста на C может возникнуть соблазн увидеть этот ответ и предположить, что, если он работает на C ++, он должен работать аналогично и на C. - person Daniel; 07.09.2011
comment
@ Даниэль: Я тоже не думаю, что мы не согласны! - person Lightness Races in Orbit; 07.09.2011
comment
Вы также можете добавить, что все эти переходы через инициализацию одинаковы для меток case. - person PlasmaHH; 07.09.2011
comment
@PlasmaHH: Думаю, вы только что это сделали! :) - person Lightness Races in Orbit; 07.09.2011
comment
Возможно, вы могли бы добавить аннотацию о том, что произойдет, если вы перескочите на вверх во время инициализации. - person Benjamin Lindley; 11.09.2011
comment
@ Бенджамин: О, я даже не подумал об этом. - person Lightness Races in Orbit; 11.09.2011
comment
Все вышеперечисленные предложения реализованы. - person Lightness Races in Orbit; 11.09.2011
comment
Вау, я только что предположил, что семантика C ++ нарушена для goto, но они на удивление разумны! Отличный ответ. - person Joseph Garvin; 13.01.2012