Domanda

Esistono alcune situazioni che lo standard C++ attribuisce come comportamento indefinito.Ad esempio, se assegno con new[], quindi prova a liberarlo con delete (non delete[]) è un comportamento indefinito - tutto può succedere - potrebbe funzionare, potrebbe bloccarsi in modo brutto, potrebbe corrompere qualcosa silenziosamente e creare un problema temporizzato.

È così problematico spiegarlo tutto può succedere parte ai neofiti.Cominciano a "provare" che "funziona" (perché funziona davvero sull'implementazione C++ che usano) e si chiedono "cosa potrebbe esserci di sbagliato in questo"?Quale spiegazione concisa potrei dare per motivarli a non scrivere tale codice?

È stato utile?

Soluzione

"Complimenti, avete definito il comportamento che ha compilatore per tale operazione. Mi aspetto la relazione sul comportamento che gli altri 200 compilatori che esistono nel mondo mostra di essere sulla mia scrivania entro le ore 10 di domani. Don 't deludermi ora, il tuo futuro sembra promettente! "

Altri suggerimenti

Non definito significa esplicitamente inaffidabile. Il software dovrebbe essere affidabile. Non si dovrebbe dire molto altro.

Un laghetto ghiacciato è un buon esempio di un piano di calpestio non definito. Solo perché si rendono attraverso una volta non significa che si dovrebbe aggiungere il collegamento al percorso carta, soprattutto se avete in programma per le quattro stagioni.

Due possibilità vengono in mente:

  1. Si potrebbe chiedere loro "solo perché si può guidare in autostrada nella direzione opposta a mezzanotte e sopravvivere, vorresti fare regolarmente?"

  2. La soluzione più coinvolti potrebbe essere quella di impostare un / ambiente diverso compilatore correre per mostrare loro come fallisce clamorosamente in circostanze diverse.

Citare semplicemente dallo standard.Se non riescono ad accettarlo, non sono programmatori C++.I cristiani negherebbero la Bibbia?;-)

1.9 Esecuzione del programma

  1. Le descrizioni semantiche nella presente norma internazionale definiscono una macchina astratta parametrizzata e non deterministica.[...]

  2. Alcuni aspetti e operazioni della macchina astratta sono descritti nella presente norma internazionale come: definito dall'implementazione (Per esempio, sizeof(int)).Questi costituiscono i parametri della macchina astratta. Ogni implementazione deve includere la documentazione che ne descrive le caratteristiche e il comportamento sotto questi aspetti. [...]

  3. Alcuni altri aspetti e operazioni della macchina astratta sono descritti nella presente norma internazionale come: non specificato (ad esempio, ordine di valutazione degli argomenti di una funzione). Ove possibile, la presente norma internazionale definisce una serie di comportamenti consentiti.Questi definiscono gli aspetti non deterministici della macchina astratta.[...]

  4. Alcune altre operazioni sono descritte nella presente norma internazionale come: non definito (ad esempio, l'effetto di dereferenziare il puntatore nullo).[ Nota: questo standard internazionale non impone requisiti sul comportamento dei programmi che contengono comportamenti indefiniti.—nota finale]

Non puoi essere più chiaro di così.

Mi piacerebbe spiegare che se non hanno scritto il codice correttamente, la loro revisione successiva prestazione non sarebbe felice. Questo è sufficiente "motivazione" per la maggior parte delle persone.

far loro provare la loro strada fino a quando il loro codice andrà in crash durante il test. Allora non saranno necessari le parole.

Il fatto è che i neofiti (siamo stati tutti lì) hanno una certa quantità di ego e fiducia in se stessi. Va bene. In realtà, non si poteva essere un programmatore, se non l'avete fatto. E 'importante educare i loro, ma non per questo meno importante sostenere loro e non tagliare il loro inizio nel viaggio da minare la loro fiducia in se stessi. Basta essere gentile ma dimostrare la propria posizione con i fatti e non con le parole. Solo i fatti e le prove funzioneranno.

In silenzio ignorare nuovo, nuovo [], delete e delete [] e vedere quanto tempo ci vuole per lui notare;)

In mancanza di ciò ... solo dirgli che è sbagliato e lui puntare verso la spec C ++. Oh yeah .. e la prossima volta di essere più attenti quando si impiegano le persone a fare in modo di evitare a-buchi!

Mi piace questa citazione:

comportamento non definito: può danneggiare i file, formattare il disco o inviare una mail di odio a il vostro capo.

Non so chi attribuire questo per (forse è da Effective C ++ )?

John Woods :

  

In breve, non è possibile utilizzare sizeof () su una struttura i cui elementi non sono stati   definiti, e se lo fai, i demoni possono volare fuori del vostro naso.

"I demoni possono volare fuori del vostro naso" semplicemente deve far parte del vocabolario di ogni programmatore.

Più precisamente, parlare di portabilità. Spiegare come i programmi hanno spesso per essere portato su diversi sistemi operativi, per non parlare di diversi compilatori. Nel mondo reale, le porte si realizza da persone diverse dai programmatori originali. Alcune di queste porte sono ancora ai dispositivi embedded, dove ci possono essere enormi costi di scoprire che il compilatore ha deciso in modo diverso dal presupposto.

Ruotare la persona in un puntatore. Dite loro che sono un puntatore a una classe umana e si invoca la funzione 'RemoveCoat'. Quando sono rivolte l'una verso una persona e dire 'RemoveCoat' tutto va bene. Se la persona non ha un cappotto, non preoccuparti - abbiamo verificare la presenza di questo, tutto RemoveCoat in realtà non fa altro che rimuovere lo strato superiore di capi di abbigliamento (con controlli di decenza).

Ora, cosa succede se sono puntate da qualche parte casuale e dicono RemoveCoat - se sono rivolte l'una verso un muro, allora la vernice potrebbe staccarsi, se sono rivolte l'una verso un albero la corteccia potrebbe venire fuori, i cani potrebbero radere se stessi, la USS Enterprise potrebbe abbassare gli scudi in un momento critico, ecc!

Non c'è modo di lavorare fuori quello che potrebbe accadere il comportamento non è stato definito per quella situazione -. Questo è chiamato comportamento non definito e deve essere evitato

C ++ non è davvero un linguaggio per dilletantes, e semplicemente messa in vendita di alcune regole e renderle obbediscono senza dubbio farà per alcuni programmatori terribili; la maggior parte delle cose più stupide che vedo la gente giudica probabilmente legato a questo tipo di regole cieche seguente / lawyering.

D'altra parte se sanno i distruttori non avranno chiamato, e forse alcuni altri problemi, poi si prenderà cura per evitarlo. E ancora più importante, avere qualche possibilità di eseguire il debug di esso, se mai farlo per caso, e anche per avere qualche possibilità di realizzare quanto sia pericoloso molte delle caratteristiche di C ++ può essere.

Dal momento che ci sono molte cose di cui preoccuparsi, nessun singolo corso o libro è mai andare a rendere qualcuno Master C ++ o probabilmente anche diventare quel bene con esso.

Uno sarebbe ...

"Questo" utilizzo non è parte del linguaggio. Se vogliamo dire che in questo caso il compilatore deve generare il codice che si blocca, allora sarebbe una caratteristica, una sorta di obbligo per il produttore del compilatore. Gli scrittori della norma non ha voluto dare lavoro inutile su "caratteristiche" che non sono supportate. Hanno deciso di non apportare alcuna requisiti di comportamento in tali casi.

Basta mostrare loro Valgrind.

Compilare ed eseguire questo programma:

#include <iostream>

class A {
    public:
            A() { std::cout << "hi" << std::endl; }
            ~A() { std::cout << "bye" << std::endl; }
};

int main() {
    A* a1 = new A[10];
    delete a1;

    A* a2 = new A[10];
    delete[] a2;
}

Almeno quando si usa GCC, mostra che il distruttore viene chiamato solo per uno degli elementi quando si fa unico di eliminazione.

A proposito di singola cancellare su array POD. li puntare a una C ++ FAQ o far eseguire il proprio codice attraverso cppcheck .

Un punto non ancora accennato circa comportamento indefinito è che se si esegue qualche operazione determinerebbe un comportamento indefinito, un'implementazione standard conformi potrebbe legittimamente, forse in uno sforzo per essere 'utile' o migliorare l'efficienza, generare il codice che sicuro se tale operazione fosse tentata. Ad esempio, si può immaginare un'architettura multiprocessore in cui qualsiasi posizione di memoria può essere bloccato, e tenta di accedere a una posizione chiusa (tranne per sbloccarlo) sarà stallo fino a quando la posizione in questione è stato sbloccato. Se il bloccaggio e lo sbloccaggio erano molto economico (plausibile se sono implementati in hardware) tale architettura potrebbe essere utile in alcuni scenari multithreading, poiché attuazione x++ come (atomicamente leggere e bloccare x, aggiungere uno a leggere il valore; atomicamente sbloccare e scrivere x) garantirebbe che se due fili entrambe eseguite x++ contemporaneamente, il risultato sarebbe di aggiungere due a x. programmi forniti sono scritti per evitare un comportamento indefinito, tale architettura potrebbe facilitare la progettazione di codice multi-threaded affidabile senza richiedere grandi barriere memoria goffo. Purtroppo, una dichiarazione come *x++ = *y++; potrebbe causare stallo se x e y erano entrambi riferimenti alla stessa posizione di memorizzazione e il compilatore tentato di gasdotto il codice come t1 = read-and-lock x; t2 = read-and-lock y; read t3=*t1; write *t2=t3; t1++; t2++; unlock-and-write x=t1; write-and-unlock y=t2;. Mentre il compilatore potrebbe evitare stallo astenendosi dal interfogliando le varie operazioni, così facendo potrebbe impedire l'efficienza.

Attiva malloc_debug e delete un array di oggetti con distruttori. freeing un puntatore all'interno del blocco dovesse guastarsi. Li chiamano tutti insieme e dimostrare questo.

È necessario pensare ad altri esempi per costruire la vostra credibilità fino a quando non capire che sono neofiti e non c'è molto da sapere su C ++.

raccontare loro standard e come gli strumenti sono stati sviluppati per rispettare gli standard. Tutto ciò al di fuori del normale potrebbe o non potrebbe funzionare, che è UB.

Solo perché il loro programma appare lavorare non è garanzia di nulla;il compilatore potrebbe generare codice che sembra funzionare (come si fa a definire "lavoro" quando il comportamento corretto è non definito?) nei giorni feriali ma formatta il disco nei fine settimana.Hanno letto il codice sorgente al loro compilatore?Esaminare la loro produzione smontata?

Oppure ricordarglielo solo perché oggi "funziona" non è garanzia che funzioni quando aggiorni la versione del compilatore.Di' loro di divertirsi a trovare qualunque sottile bug si insinui da questo.

E davvero, perché non?Dovrebbero fornire un argomento giustificabile per utilizzare un comportamento indefinito, non viceversa.Che motivo c'è per usarlo delete invece di delete[] altro che pigrizia?(Va bene, c'è std::auto_ptr.Ma se stai usando std::auto_ptr con un new[]-allocated array, probabilmente dovresti usare a std::vector Comunque.)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top