Domanda

Cos'è il comportamento indefinito in C e C++?Che dire del comportamento non specificato e del comportamento definito dall'implementazione?Qual'è la differenza tra loro?

È stato utile?

Soluzione

Comportamento indefinito è uno di quegli aspetti dei linguaggi C e C++ che possono sorprendere i programmatori provenienti da altri linguaggi (altri linguaggi cercano di nasconderlo meglio).Fondamentalmente è possibile scrivere programmi C++ che non si comportano in modo prevedibile, anche se molti compilatori C++ non segnalano alcun errore nel programma!

Consideriamo un classico esempio:

#include <iostream>

int main()
{
    char* p = "hello!\n";   // yes I know, deprecated conversion
    p[0] = 'y';
    p[5] = 'w';
    std::cout << p;
}

La variabile p punta alla stringa letterale "hello!\n", e i due compiti seguenti provano a modificare quella stringa letterale.Cosa fa questo programma?Secondo la sezione 2.14.5 paragrafo 11 dello standard C++, invoca comportamento indefinito:

L'effetto del tentativo di modificare una stringa letterale non è definito.

Posso sentire la gente urlare "Ma aspetta, posso compilarlo senza problemi e ottenere l'output yellow" o "Cosa intendi con non definito, i valori letterali stringa sono archiviati nella memoria di sola lettura, quindi il primo tentativo di assegnazione risulta in un core dump".Questo è esattamente il problema con il comportamento indefinito.Fondamentalmente, lo standard consente che succeda di tutto una volta invocato un comportamento indefinito (anche i demoni nasali).Se esiste un comportamento "corretto" secondo il tuo modello mentale del linguaggio, quel modello è semplicemente sbagliato;Lo standard C++ ha l'unico voto, punto.

Altri esempi di comportamento indefinito includono l'accesso a un array oltre i suoi limiti, dereferenziare il puntatore nullo, accedere agli oggetti dopo la fine della loro vita o scrivere espressioni presumibilmente intelligenti Piace i++ + ++i.

La sezione 1.9 dello standard C++ menziona anche i due fratelli meno pericolosi del comportamento indefinito, comportamento non specificato E comportamento definito dall'implementazione:

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

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.

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.

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 ]

Nello specifico, la sezione 1.3.24 afferma:

Il comportamento indefinito consentito varia da ignorando completamente la situazione con risultati imprevedibili, al comportamento durante la traduzione o l'esecuzione del programma in modo documentato caratteristico dell'ambiente (con o senza l'emissione di un messaggio diagnostico), al termine di una traduzione o esecuzione (con l'emissione di un messaggio diagnostico).

Cosa puoi fare per evitare di incorrere in comportamenti indefiniti?Fondamentalmente devi leggere buoni libri sul C++ da autori che sanno di cosa parlano.Fanculo i tutorial su internet.Vite bullschildt.

Altri suggerimenti

Bene, questo è fondamentalmente un semplice copia-incolla dallo standard

3.4.1 1 comportamento definito dall'implementazione comportamento non specificato in cui ogni implementazione documenta il modo in cui viene effettuata la scelta

2 Esempio Un esempio di comportamento definito dall'implementazione è la propagazione del bit di alto ordine quando un numero intero firmato è spostato a destra.

3.4.3 1 comportamento indefinito comportamento, all'uso di un costrutto di programma non portabile o errato o di dati errati, per i quali questo standard internazionale non impone requisiti

2 Nota possibili comportamenti non definiti spazia dall'ignorare completamente la situazione con risultati imprevedibili, a comportarsi durante la traduzione o l'esecuzione del programma in modo documentato caratteristico dell'ambiente (con o senza l'emissione di un messaggio diagnostico), a terminare una traduzione o un'esecuzione (con l'emissione di un messaggio diagnostico).

3 Esempio Un esempio di comportamento indefinito è il comportamento sull'overflow intero.

3.4.4 1 comportamento non specificato Uso di un valore non specificato o altro comportamento in cui questo standard internazionale fornisce due o più possibilità e non impone ulteriori requisiti su quali siano scelti in qualsiasi caso

2 Esempio Un esempio di comportamento non specificato è l'ordine in cui vengono valutati gli argomenti di una funzione.

Forse una formulazione semplice potrebbe essere più facile da comprendere rispetto alla definizione rigorosa degli standard.

comportamento definito dall'implementazione
Il linguaggio dice che abbiamo tipi di dati.I fornitori del compilatore specificano quali dimensioni devono utilizzare e forniscono una documentazione di ciò che hanno fatto.

comportamento indefinito
Stai facendo qualcosa di sbagliato.Ad esempio, hai un valore molto grande in an int quello non si adatta char.Come inserisci quel valore? char?in realtà non c'è modo!Potrebbe succedere di tutto, ma la cosa più sensata sarebbe prendere il primo byte di quell'int e inserirlo char.È semplicemente sbagliato farlo per assegnare il primo byte, ma questo è ciò che accade dietro le quinte.

comportamento non specificato
Quale delle due funzioni viene eseguita per prima?

void fun(int n, int m);

int fun1()
{
  cout << "fun1";
  return 1;
}
int fun2()
{
  cout << "fun2";
  return 2;
}
...
fun(fun1(), fun2()); // which one is executed first?

La lingua non specifica la valutazione, da sinistra a destra o da destra a sinistra!Quindi un comportamento non specificato può o meno risultare in un comportamento indefinito, ma certamente il tuo programma non dovrebbe produrre un comportamento non specificato.


@eSKay Penso che valga la pena modificare la risposta alla tua domanda per chiarire di più :)

per fun(fun1(), fun2()); Il comportamento non è "definito l'implementazione"?Il compilatore deve scegliere l'uno o l'altro corso, dopo tutto?

La differenza tra definito dall'implementazione e non specificato è che il compilatore dovrebbe scegliere un comportamento nel primo caso ma non è obbligato a farlo nel secondo caso.Ad esempio, un'implementazione deve avere una ed una sola definizione di sizeof(int).Quindi non si può dire questo sizeof(int) è 4 per alcune parti del programma e 8 per altre.A differenza del comportamento non specificato, in cui il compilatore può dire OK, valuterò questi argomenti da sinistra a destra e gli argomenti della funzione successiva verranno valutati da destra a sinistra.Può succedere nello stesso programma, ecco perché si chiama non specificato.In effetti, il C++ avrebbe potuto essere reso più semplice se fossero stati specificati alcuni dei comportamenti non specificati.Dai un'occhiata qui Dott.La risposta di Stroustrup a questo:

Si sostiene che la differenza tra ciò che può essere prodotto dando al compilatore questa libertà e richiedere una "valutazione ordinaria da sinistra a destra" può essere significativa.Non sono convinto, ma con innumerevoli compilatori "là fuori" che approfittano della libertà e alcune persone che difendono con passione quella libertà, un cambiamento sarebbe difficile e potrebbe richiedere decenni per penetrare negli angoli lontani dei mondi C e C ++.Sono deluso dal fatto che non tutti i compilatori mettono in guardia contro il codice come ++ i+i ++.Allo stesso modo, l'ordine di valutazione degli argomenti non è specificato.

IMO troppe "cose" sono lasciate indefinite, non specificate, definite dall'implementazione, ecc.Tuttavia, è facile da dire e persino per dare esempi, ma difficile da fissare.Va anche notato che non è così difficile evitare la maggior parte dei problemi e produrre codice portatile.

Dal documento ufficiale di motivazione C

I termini non specificato comportamento, non definito comportamento e definito dall'implementazione I comportamenti vengono utilizzati per classificare il risultato della scrittura di programmi le cui proprietà lo standard non descrive o non può descrivere completamente.L'obiettivo dell'adozione di questa categorizzazione è quello di consentire una certa varietà tra le implementazioni che consenta alla qualità dell'implementazione di essere una forza attiva sul mercato, nonché di consentire alcune estensioni popolari, senza rimuovere il prestigio della conformità allo standard.L'Appendice F dello Standard cataloga quei comportamenti che rientrano in una di queste tre categorie.

Comportamento non specificato dà all'implementatore una certa libertà nella traduzione dei programmi.Questa latitudine non si estende fino alla mancata traduzione del programma.

Comportamento indefinito dà all'implementatore la licenza di non rilevare alcuni errori del programma difficili da diagnosticare.Identifica inoltre aree di possibile estensione linguistica conforme:l'implementatore può aumentare il linguaggio fornendo una definizione del comportamento ufficialmente indefinito.

Definito dall'implementazione Il comportamento dà all'implementatore la libertà di scegliere l'approccio appropriato, ma richiede che questa scelta sia spiegata all'utente.I comportamenti designati come definiti dall'implementazione sono generalmente quelli in cui un utente può prendere decisioni di codifica significative in base alla definizione di implementazione.Gli implementatori dovrebbero tenere presente questo criterio quando decidono quanto estesa dovrebbe essere una definizione di implementazione.Come nel caso del comportamento non specificato, semplicemente non riuscire a tradurre la fonte contenente il comportamento definito dall'implementazione non è una risposta adeguata.

indefinito Comportamento vs. comportamento non specificato ha una breve descrizione di esso.

La loro sintesi finale:

  

In sintesi, il comportamento non specificato di solito è qualcosa che non dovrebbe   preoccuparsi, a meno che il software è necessario per essere portatile.   Al contrario, un comportamento indefinito è sempre auspicabile e non dovrebbe mai   verificarsi.

Storicamente, sia il comportamento definito dall'implementazione e non definita Comportamento rappresentato situazioni in cui della norma gli autori si aspettavano che le persone che scrivono le implementazioni di qualità userebbero giudizio per decidere quali garanzie comportamentali, se del caso, sarebbe utile per i programmi nel campo di applicazione previsto in esecuzione sugli obiettivi previsti. I bisogni di fascia alta codice elaborazione di calcoli numerici sono molto diverse da quelle del codice di sistemi di basso livello, ed entrambi UB e IDB danno scrittori compilatore flessibilità per soddisfare tali esigenze diverse. Né mandati di categoria che le implementazioni si comportano in un modo che è utile per uno scopo particolare, o anche per qualsiasi scopo. implementazioni di qualità che pretendono di essere adatto per un particolare scopo, tuttavia, dovrebbero comportarsi in maniera rispondente tale scopo se il Principio richiede o no .

L'unica differenza tra il comportamento definito dall'implementazione e comportamento non definito è che il primo richiede che le implementazioni definire e documentare un comportamento coerente , anche nei casi in cui nulla l'attuazione potrebbe fare sarebbe utile . La linea di demarcazione tra i due non è se sarebbe generalmente essere utile per le implementazioni per definire i comportamenti (scrittori compilatore dovrebbe definire comportamenti utili quando pratico se la norma impone loro o no), ma se ci potrebbe essere implementazioni in cui definiscono un comportamento avrebbe essere contemporaneamente costosa e inutile . Un giudizio che si presentino tali implementazioni non fa in alcun modo, forma o forma, comporta alcun giudizio circa l'utilità di sostenere un comportamento definito su altre piattaforme.

Purtroppo, dal momento che metà degli anni 1990 scrittori del compilatore hanno iniziato a interpretare la mancanza di mandati comportamentali come un giudizio che garanzie comportamentali non valgono il costo, anche in campi di applicazione dove sono di vitale importanza, e anche su sistemi in cui costano praticamente Niente. Invece di trattare UB come un invito a esercitare il giudizio ragionevole, gli scrittori del compilatore hanno iniziato a trattarlo come una scusa non di farlo.

Ad esempio, dato il seguente codice:

int scaled_velocity(int v, unsigned char pow)
{
  if (v > 250)
    v = 250;
  if (v < -250)
    v = -250;
  return v << pow;
}

un'implementazione complemento a due non avrebbe spendere alcuno sforzo sorta per trattare la v << pow espressione come un cambiamento in complemento a due senza riguardo per se v è stato positivo o negativo.

La filosofia preferito tra alcuni degli scrittori del compilatore di oggi, tuttavia, suggerisce che a causa v non può che essere negativo se il programma sta per impegnarsi in un comportamento indefinito, non c'è alcun motivo per avere il programma clip di campo negativo di v. Anche se ha lasciato mutare dei valori negativi utilizzati per essere supportati su ogni singolo compilatore di importanza, e una grande quantità di codice esistente si basa su tale comportamento, la filosofia moderna avrebbe interpretato il fatto che la norma dice che a sinistra-shifting valori negativi è UB come il che implica che gli scrittori del compilatore dovrebbe sentirsi libero di ignorare questo.

Attuazione definita-

Gli implementatori desiderano, dovrebbero essere ben documentati, lo standard offre scelte ma sicuramente da compilare

Non specificato -

Uguale a quello definito dall'implementazione ma non documentato

Non definito-

Potrebbe succedere di tutto, prenditene cura.

Norma C++ n3337 § 1.3.10 comportamento definito dall'implementazione

comportamento, per un programma di programma ben formato e correggere dati, che dipende dall'implementazione e che ciascuna implementazione documenta

A volte il C++ Standard non impone un comportamento particolare su alcuni costrutti ma dice invece che deve essere scelto un comportamento particolare e ben definito e descritto per particolare implementazione (versione della libreria).Quindi l'utente può ancora sapere esattamente come si comporterà il programma anche se Standard non lo descrive.


Norma C++ n3337 § 1.3.24 comportamento indefinito

comportamento per il quale questo standard internazionale non impone requisiti [NOTA:Ci si può aspettare un comportamento indefinito quando questo standard internazionale omette qualsiasi definizione esplicita di comportamento o quando un programma utilizza un costrutto errato o dati errati.Il comportamento non definito consentito va dall'ignorare completamente la situazione con risultati imprevedibili, a comportarsi durante la traduzione o l'esecuzione del programma in modo documentato caratteristico dell'ambiente (con o senza l'emissione di un messaggio diagnostico), a terminare una traduzione o un'esecuzione (con l'emissione di un messaggio diagnostico).Molti costrutti di programma errati non generano comportamenti non definiti;devono essere diagnosticati.— nota finale]

Quando il programma incontra un costrutto che non è definito secondo lo standard C++, gli è consentito fare tutto ciò che vuole (magari inviarmi un'e-mail o magari inviarti un'e-mail o magari ignorare completamente il codice).


Norma C++ n3337 § 1.3.25 comportamento non specificato

Comportamento, per un costrutto di programma ben formato e dati corretti, che dipende dall'implementazione [NOTA:L'implementazione non è necessaria per documentare quale comportamento si verifica.La gamma di comportamenti possibili è generalmente delineata da questo standard internazionale.- End Nota

Il C++ Standard non impone un comportamento particolare su alcuni costrutti ma dice invece che deve essere scelto un comportamento particolare e ben definito ( bot non necessariamente descritto) per particolare implementazione (versione della libreria).Quindi, nel caso in cui non sia stata fornita alcuna descrizione, può essere difficile per l'utente sapere esattamente come si comporterà il programma.

Ci sono molti costrutti che dovrebbe comportarsi in un modo utile e prevedibile, in alcuni casi, ma non possono in pratica essere apportate a farlo in tutti i casi su tutte le implementazioni. Spesso, l'insieme di casi per i quali un costrutto deve essere utilizzabile dipenderà dalla piattaforma di destinazione e il campo di applicazione. Perché implementazione per differenti obiettivi e campi deve gestire diverse serie di casi, lo Standard vede la questione di quali casi da gestire come una qualità di problema di implementazione. Inoltre, a causa della norma autori vedevano alcuna necessità di vietare "conforme" implementazioni di essere di pessima qualità da essere inutili, che spesso non si preoccupano di mandato esplicitamente il comportamento dei casi si aspettavano che tutte le implementazioni non di immondizia al supporto anche senza un mandato.

Per esempio, il codice:

struct foo {int x;} = {0};
int main(void)
{
  foo.x = 1;
  return foo.x-1;
}

utilizza un lvalue di tipo int [cioè foo.x] per accedere al valore memorizzato di un oggetto di tipo struct foo, anche se N1570 6.5p7 contiene nulla che consentirebbe un oggetto del tipo struct foo a cui accedere se non attraverso un lvalue di tipo struct foo o un Ivalue di un tipo di carattere, né la standard contengono qualsiasi linguaggio che avrebbe esentare le espressioni struct membri di accesso dai requisiti di 6.5p7.

Ovviamente qualsiasi compilatore che non può gestire semplici espressioni struct membri di accesso dovrebbe essere considerato come eccezionalmente bassa qualità e probabilmente non adatto a granché. Di conseguenza, dovrebbe essere ragionevole aspettarsi che tutti coloro che cercano di produrre una realizzazione di qualità sosterrà tale costrutto indipendentemente o meno i mandati standard IT. A condizione che gli scrittori del compilatore poteva fidare make una buona fede sforzo per compilatori di qualità dei prodotti che sono adatti per i loro scopi previsti, ed essere aperti circa gli scopi per i quali i loro compilatori sono o non sono adatti, non ci sarebbe alcun motivo per avere l'inchiostro di scarto standard cercando di cose di stato che dovrebbe essere ovvio. Molte azioni che dovrebbero avere comportamenti utilizzabili e prevedibili sono, in effetti, non definito Comportamento a causa della standard autori fiducia scrittori del compilatore di esercitare un giudizio ragionevole, piuttosto che utilizzare il fatto che le azioni invoke comportamento non definito come una scusa per gettare giudizio fuori dalla finestra .

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