Чи буде використання змінних goto leak?


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.
Ben Voigt,

Чому б не змінити запитання на щось на зразок "У наведеному прикладі, чи буде шлях виконання коду переноситися з 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]:Можна перенести в блок, але не таким чином, щоб обійти оголошення з ініціалізацією. Програма, яка переходить від точки, де змінна з автоматичною тривалістю зберігання не знаходиться в області дії, до точки, де вона знаходиться в області дії, неправильно сформована, якщо змінна не має скалярного типу, типу класу з тривіальним конструктором за замовчуванням і тривіальним деструктором, 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
@Daniel: Питання та відповідь дуже конкретно стосуються C ++, але справедливо. Можливо, ми можемо отримати ще один FAQ, який розвіює міф про те, що C і C ++ - це одне і те ж;)
Гонки легкості на орбіті

3
@Tomalak: Я не думаю, що ми тут розходимось. Багато відповідей, поданих на SO, десь явно задокументовані. Я просто підкреслював, що для програміста C може бути спокусливим побачити цю відповідь і припустити, що якщо він працює на C ++, він повинен працювати аналогічно на C.
Daniel,

2
Ви також можете додати, що всі ці речі, що перескакують через ініціалізацію, однакові для міток регістрів.
PlasmaHH

12
Ого, я щойно припустив, що семантика C ++ порушена для goto, але вони напрочуд здорові! Чудова відповідь.
Джозеф Гарвін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.