Domanda

Qualcuno può spiegarmi perché questo codice stampa 14?Mi è stato appena chiesto da un altro studente e non sono riuscito a capirlo.

int i = 5;
i = ++i + ++i;
cout<<i;
È stato utile?

Soluzione

L'ordine degli effetti collaterali non è definito in C ++. Inoltre, la modifica di una variabile due volte in una singola espressione non ha un comportamento definito (vedere standard C ++ , & # 167; 5.0.4, pagina fisica 87 / pagina logica 73).

Soluzione: non usare effetti collaterali nell'espressione complessa, non usarne più di uno in quelli semplici. E non fa male abilitare tutti gli avvisi che il compilatore può darti: l'aggiunta di -Wall (gcc) o /Wall /W4 (Visual C ++) alla riga di comando genera un avviso di adattamento:

test-so-side-effects.c: In function 'main':
test-so-side-effects.c:5: warning: operation on 'i' may be undefined
test-so-side-effects.c:5: warning: operation on 'i' may be undefined

Ovviamente, il codice viene compilato in:

i = i + 1;
i = i + 1;
i = i + i;

Altri suggerimenti

Questo è un comportamento indefinito, il risultato varierà a seconda del compilatore che usi. Vedi, ad esempio, C ++ FAQ Lite .

In alcune risposte/commenti si è discusso del significato di "comportamento indefinito" e se ciò renda il programma non valido.Quindi sto pubblicando questa risposta piuttosto lunga che dettaglia esattamente cosa dice lo standard con alcune note.Spero che non sia troppo noioso...

Le parti dello standard citate provengono dall'attuale standard C++ (ISO/IEC 14882:2003).C'è una formulazione simile nello standard C.

Secondo lo standard C++, la modifica di un valore più di una volta all'interno di una serie di punti di sequenza comporta un comportamento indefinito (sezione 5 paragrafo 4):

Salvo ove indicato, l'ordine di valutazione degli operandi dei singoli operatori e le sottoespressioni delle singole espressioni e l'ordine in cui si verificano gli effetti collaterali, non è specificato.53) tra il punto di sequenza precedente e successivo che un oggetto scalare deve avere il suo valore memorizzato modificato al massimo una volta dalla valutazione di un'espressione.Inoltre, il valore iniziale è pari a: accessibile solo per determinare il valore da immagazzinare.I requisiti del presente regolamento paragrafo deve essere rispettato per ogni ordinamento ammissibile del sottoespressioni di un'espressione completa;altrimenti il ​​comportamento è indefinito.[Esempio:

i = v[i++]; // the behavior is unspecified
i = 7, i++, i++; // i becomes 9
i = ++i + 1; // the behavior is unspecified
i = i + 1; // the value of i is incremented

—fine esempio]

Tieni presente che il secondo esempio, "i = 7, i++, i++;" è definito poiché l'operatore virgola è un punto di sequenza.

Ecco cosa significa secondo lo standard C++ "comportamento indefinito":

1.3.12 comportamento indefinito [defns.unfined]

comportamento, come potrebbe sorgere dall'uso di un costrutto di programma errato o di dati errati, per il quale questo La norma internazionale non impone requisiti.Un comportamento indefinito può anche essere previsto quando questo La norma internazionale omette di descrivere qualsiasi definizione esplicita di comportamento.[Nota:consentito indefinito comportamento varia da ignorare la situazione completamente con risultati imprevedibili, a comportarsi durante traduzione o esecuzione di programmi in modo documentato caratteristico dell'ambiente (con o senza l'emissione di un messaggio diagnostico), alla fine di una traduzione o di un'esecuzione (con l'emissione di un messaggio diagnostico) messaggio diagnostico).Molti costrutti di programma errati non generano comportamenti indefiniti;devono essere diagnosticati.]

In altre parole, il compilatore è libero di fare quello che vuole, incluso

  1. sputando un messaggio di errore,
  2. fare qualcosa di implementativo definito e documentato,
  3. avere risultati del tutto imprevedibili

Il secondo punto riguarda le estensioni del linguaggio di cui dispone la maggior parte dei compilatori, ma ovviamente non sono definite nello standard.

Quindi immagino che in senso stretto qualcosa che mostra un comportamento indefinito non sia "illegale", ma nella mia esperienza ogni volta che c'è stato qualcosa in un programma C/C++ che mostra un "comportamento indefinito" (a meno che non sia un'estensione) - è un bug.Penso che definire illegale un simile costrutto non sia fonte di confusione, fuorviante o fuorviante.

Inoltre, penso che cercare di spiegare cosa sta facendo il compilatore per raggiungere il valore 14 non sia particolarmente utile, poiché non coglie il punto.Il compilatore potrebbe fare quasi qualsiasi cosa;infatti, è probabile che il compilatore possa raggiungere un risultato diverso quando viene eseguito utilizzando opzioni di ottimizzazione diverse (o può produrre codice che si blocca - chi lo sa?).

Per coloro che desiderano ulteriori riferimenti o un appello all'autorità, ecco alcuni suggerimenti:

La lunga, lunga risposta di Steve Summit (manutentore delle domande frequenti su comp.lang.c) su questo argomento del 1995:

Ecco cosa ha da dire Bjarne Stroustrup sull'argomento:


Nota:lo standard C++ usa la parola 'illegale' esattamente una volta - quando descrive una differenza tra C++ e Standard C riguardo all'uso di static O extern con dichiarazioni di tipo.

Semplice ... il compilatore sta valutando gli incrementi ENTRAMBI prima di eseguire la somma, senza memorizzare nella cache i risultati intermedi. Ciò significa che quando aggiungi due volte, ora ha il valore di 7.

Se lo fai

int j=++i; 
int k=++i;

i = j+k;

vedrai 13 come previsto.

Sul tuo particolare compilatore, sta scegliendo di eseguire prima entrambe le operazioni ++, quindi l'aggiunta. Interpreta il codice come:

int i = 5;
++i;
++i;
i = i + i;
cout << i;

È perfettamente valido.

Una domanda migliore sarebbe, sarà sempre 14?

int i = 5;
i = ++i + ++i;
cout<<i;

i = ++i   + ++i   ;
i = ++(5) + ++(5) ;
i =    6  +    6  ;
i = 12;

i = ++i   + ++i   ;
i = ++i   + ++(5) ;
i = ++i   +   (6) ;
i = ++(6) +    6  ;
i =   (7) +    6  ;
i = 13;

i = ++i   + ++i   ;
i = ++i   + ++(5) ;
i = ++(6) +   (6) ;
i =   (7) +   (7) ;
i = 14;

Probabilmente sarà <=>, perché ha leggermente più sensato.

Penso che quando si osserva il problema dalla vista dell'albero della sintassi, la risposta al problema diventa più chiara:

I
|
=
|
+
|
espressione unaria - espressione unaria

espressione unaria: espressione unaria dell'operatore

Nel nostro caso, l'espressione si riduce alla variabile i.

Ora ciò che accade è che entrambe le espressioni unarie modificano lo stesso operando, quindi il codice fa due volte ++ i quando valuta le espressioni unarie prima di aggiungere i risultati di entrambe le espressioni unarie.

Quindi quello che fa il codice è davvero

++ i;
++ i;
i = i + i;

Per i = 5 significa che

i = i + 1; // i < - 6
i = i + 1; // i < - 7
i = i + i; // i < - 14

Perché l'incremento del prefisso ha la precedenza:

int i = 5;
i = i+1; // First ++i, i is now 6
i = i+1; // Second ++i, i is now 7
i = i + i // i = 7 + 7
cout << i // i = 14
 i = i++ + i; //11  

 i = i++ + i++; //12

 i = i++ + ++i; //13

 i = ++i + i++; //13

 i = ++i + ++i; //14    
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top