Userà le variabili goto leak?


94

È vero che gotosalta attraverso bit di codice senza chiamare distruttori e cose del genere?

per esempio

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

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

Non xsarà trapelato?


Correlati: stackoverflow.com/questions/1258201/… (ma volevo farlo da zero, in modo pulito!)
Lightness Races in Orbit

15
Cosa "Won't x be leaked"significa? Il tipo di xè un tipo di dati incorporato. Perché non scegli un esempio migliore?
Nawaz

2
@Nawaz: L'esempio è perfetto così com'è. Quasi ogni volta che ne parlo con qualcuno goto, pensano che anche le variabili di durata della memorizzazione automatica siano in qualche modo "trapelate". Che tu ed io sappiamo diversamente è completamente fuori questione.
Gare di leggerezza in orbita

1
@ David: sono d'accordo che questa domanda ha molto più senso quando la variabile ha un distruttore non banale ... e ho guardato nella risposta di Tomalak e ho trovato un esempio del genere. Inoltre, mentre un intnon può perdere, può essere trapelato . Ad esempio: void f(void) { new int(5); }perde un file int.
Ben Voigt

Perché non cambiare la domanda in qualcosa del tipo "Nell'esempio fornito, il percorso di esecuzione del codice verrà trasferito da f () a main () senza cancellare lo stack e altre funzionalità di ritorno dalla funzione? Importerebbe se fosse necessario un distruttore chiamato? È lo stesso in C? " Ciò manterrebbe entrambi l'intento della domanda, evitando al contempo i possibili malintesi?
Jack V.

Risposte:


210

Avvertenza: questa risposta riguarda solo C ++ ; le regole sono abbastanza diverse in C.


Non xsarà trapelato?

No, assolutamente no.

È un mito che gotoè un costrutto di basso livello che consente di sovrascrivere i meccanismi di scoping incorporati di C ++. (Se non altro, longjmppotrebbe essere incline a questo.)

Considera i seguenti meccanismi che ti impediscono di fare "cose ​​cattive" con le etichette (che includono le caseetichette).


1. Ambito dell'etichetta

Non puoi saltare tra le funzioni:

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

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

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

[n3290: 6.1/1]:[..] Lo scopo di un'etichetta è la funzione in cui appare. [..]


2. Inizializzazione dell'oggetto

Non puoi saltare attraverso l'inizializzazione degli oggetti:

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

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

Se torni indietro attraverso l'inizializzazione dell'oggetto, la precedente "istanza" dell'oggetto viene distrutta :

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]:[..] Il trasferimento fuori da un loop, da un blocco o indietro oltre una variabile inizializzata con durata di memorizzazione automatica comporta la distruzione di oggetti con durata di memorizzazione automatica che sono nell'ambito nel punto trasferito da ma non nel punto trasferito a . [..]

Non puoi saltare nell'ambito di un oggetto, anche se non è inizializzato esplicitamente:

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

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

... ad eccezione di alcuni tipi di oggetti , che il linguaggio può gestire indipendentemente perché non richiedono una costruzione "complessa":

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

// OK

[n3290: 6.7/3]:È possibile trasferire in un blocco, ma non in un modo che aggiri le dichiarazioni con inizializzazione. Un programma che salta da un punto in cui una variabile con durata di memorizzazione automatica non è nell'ambito di applicazione a un punto in cui è nell'ambito è mal formato a meno che la variabile non abbia un tipo scalare, un tipo di classe con un banale costruttore predefinito e un banale distruttore, un cv-qualificato versione di uno di questi tipi o un array di uno dei tipi precedenti e viene dichiarato senza un inizializzatore. [..]


3. Il salto si attiene all'ambito di altri oggetti

Allo stesso modo, gli oggetti con durata di archiviazione automatica non vengono "trapelati" quando esci dal loro ambitogoto :

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

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

lol:
   return 0;
}

// *T~T

[n3290: 6.6/2]:All'uscita da un ambito (comunque realizzato), gli oggetti con durata di memorizzazione automatica (3.7.3) che sono stati costruiti in tale ambito vengono distrutti nell'ordine inverso rispetto alla loro costruzione. [..]


Conclusione

I meccanismi di cui sopra garantiscono che gotonon ti permetta di rompere la lingua.

Naturalmente, questo non significa automaticamente che si "dovrebbe" uso gotoper un dato problema, ma lo fa significa che non è quasi come "male", come i comuni derivazioni mito le persone a credere.


8
Potresti notare che C non impedisce che tutte queste cose pericolose accadano.
Daniel

13
@ Daniel: la domanda e la risposta riguardano molto specificamente il C ++, ma il punto è giusto. Forse possiamo avere un'altra FAQ che dissipa il mito secondo cui C e C ++ sono la stessa cosa;)
Gare di leggerezza in orbita

3
@ Tomalak: Non credo che siamo in disaccordo qui. Molte delle risposte fornite su SO sono esplicitamente documentate da qualche parte. Stavo solo dicendo che potrebbe essere allettante per un programmatore C vedere questa risposta e presumere che se funziona in C ++, dovrebbe funzionare allo stesso modo in C.
Daniel

2
Potresti anche aggiungere che tutte queste cose di inizializzazione saltellanti sono le stesse per le etichette dei casi.
PlasmaHH

12
Wow, avevo appena pensato che la semantica di C ++ fosse rotta per goto, ma sono sorprendentemente sani! Bella risposta.
Joseph Garvin
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.