2009-10-22 17 views
5

Perché questo esempio di codice si comporta in modo diverso in C++ e C#.L'indice, l'assegnazione e l'incremento in una dichiarazione si comportano in modo diverso in C++ e C#. Perché?

[C++ Esempio]

int arr[2]; 
int index = 0; 
arr[index] = ++index; 

il cui risultato sarà arr [1] = 1;

[C# Esempio]

int[] arr = new int[2]; 
int index = 0; 
arr[index] = ++index; 

il cui risultato sarà arr [0] = 1;

Lo trovo molto strano. Sicuramente ci deve essere qualche razionale per entrambe le lingue per implementarlo in modo diverso? Mi chiedo quale sarebbe l'output C++/CLI?

+0

Beh, jynx, ragazzi. ;) – hobbs

risposta

11

Come altri hanno notato, il comportamento di questo codice è non definito in C/C++. Puoi ottenere qualsiasi risultato.

Il comportamento del codice C# è rigorosamente definite dalla C# standard.

Sicuramente ci deve essere qualche logica per entrambe le lingue per implementarlo in modo diverso?

Bene, supponiamo che stiate progettando C# e desiderate rendere il linguaggio facile da imparare per i programmatori C++. Sceglieresti di copiare l'approccio di C++ a questo problema, cioè lasciarlo indefinito? Vuoi veramente rendere facile agli sviluppatori perfettamente intelligenti scrivere accidentalmente del codice che il compilatore può solo creare un significato per quello che vuole?

I progettisti di C# non credono che il comportamento indefinito delle espressioni semplici sia una buona cosa, e quindi abbiamo definito rigorosamente le espressioni come questo. Non possiamo assolutamente concordare con ciò che ogni compilatore C++ fa perché diversi compilatori C++ danno risultati diversi per questo tipo di codice, e quindi non possiamo essere d'accordo con tutti loro.

Per quanto riguarda il motivo per cui i progettisti di C++ credono che sia meglio lasciare espressioni semplici come questa per avere un comportamento indefinito, beh, dovrete chiedere a uno di loro. Potrei certamente fare alcune congetture, ma quelle sarebbero solo ipotesi plausibili.

Ho scritto un numero di articoli di blog su questo tipo di problema; la mia più recente riguardava quasi esattamente il codice che menzioni qui.Alcuni articoli si potrebbe desiderare di leggere:

Come la progettazione di C# favorisce l'eliminazione di bug sottili:

http://blogs.msdn.com/ericlippert/archive/2007/08/14/c-and-the-pit-of-despair.aspx

Esattamente quello che è il rapporto tra la precedenza, associatività, e l'ordine di esecuzione in C#?

http://blogs.msdn.com/ericlippert/archive/2008/05/23/precedence-vs-associativity-vs-order.aspx

In che ordine fanno gli effetti collaterali di indicizzazione, l'assegnazione e l'incremento accada?

http://blogs.msdn.com/ericlippert/archive/2009/08/10/precedence-vs-order-redux.aspx

+0

Grazie. Leggerò gli articoli. –

+0

Non è definito in C++ perché è anche indefinito in C. Apparentemente, i compilatori C diversi hanno fatto cose diverse prima che lo standard venisse adottato e lasciandolo indefinito era l'unica opzione ragionevole, IMHO. –

3

Il comportamento dell'utilizzo dell'indice e dell'indice ++ all'interno dello stesso compito non è specificato in C++. Non puoi semplicemente farlo: scrivi arr[index] = index + 1 e successivamente incrementa la tua variabile. Del resto, con il mio compilatore C++ sulla mia macchina vedo arr [0] = 1, e arr [1] non è toccato.

+6

È peggio di non specificato, è in realtà un comportamento non definito. –

+0

Ovviamente, è quello che avrei dovuto scrivere (è * specificato * come * undefined * ;-) Non riesco a mantenere una mente corretta quando passi un'intera mattinata di debug ... (Ho persino trovato un bug in gdb!) –

4

Il tuo codice C++ potrebbe, in effetti, fare qualsiasi cosa. arr[index] = ++index; richiama il comportamento non definito.

0

Il risultato della versione C++ non sarà sempre come si scrive mentre si invoca il comportamento non definito. In C++ si ottiene comportamento non definito se si utilizza il valore di una variabile in un'espressione quando tale variabile viene modificata anche la stessa espressione a meno che la lettura di tale valore non sia parte della determinazione del valore da scrivere o l'espressione contenga una sequenza punto tra la lettura e la scrittura.

Nella tua espressione, Stai leggendo il valore di index per determinare dove assegnare il risultato del lato destro della =, ma il sub-espressione mano destra modifica anche index.

1

Nel caso del C++, almeno, si sta invocando un comportamento non definito preincrementando e utilizzando index senza un punto di sequenza intermedio. Se alimenti che il codice a GCC con avvisi permesso dirà:

preinc.cpp:6: warning: operation on ‘index’ may be undefined 

Sto indovinando che è indefinito e in C#, ma non so la lingua. Per C e C++, comunque, la risposta è che il compilatore può fare tutto ciò che vuole senza essere sbagliato perché il tuo codice è errato. Non c'è alcun obbligo per i diversi compilatori (o anche lo stesso compilatore) di produrre coerenti con i risultati.

+0

Proprio come una curiosità, G ++ 4.4.1 si comporta come C#, impostando l'array [0] = 1. – hobbs

+1

_Capitolo non definito_ significa che potrebbe impostare 'array [1] = 1' o impostare' array [4711] = 42', pulire il tuo hard disco, o diventa calvo, a seconda che sia presente un debugger, è la quarta volta, a seconda della fase della luna, quanto ti piace la tua ragazza o se sei stato cattivo con tua madre. Non ha senso dire "compilatore x, versione y, fa questo modo", perché non si sa mai. (http://stackoverflow.com/questions/1553382/1553407#1553407) – sbi

+4

È * STRETTAMENTE DEFINITO * in C#. Non entriamo in questo folle mondo di espressioni semplici che non hanno significato, ma il compilatore le prende comunque e fa qualcosa di pazzo. Dacci un po 'di credito qui! –

1

Nota: in base allo @Eric Lippert di answer, il comportamento è strettamente definito per C#, quindi consentimi di riformulare la mia risposta.

Questo codice:

arr[index] = ++index; 

è difficile da leggere, anche se il compilatore C# sa esattamente come valutare e in quale ordine. Solo per questo motivo dovrebbe essere evitato.

Il numero MSDN page su C# Operators si spinge fino a dire che questo comportamento potrebbe essere indefinito, anche se Eric sottolinea che non lo è. Il fatto che più fonti di documentazione (mi fiderò di Eric su questo comunque) lo rende diverso è anche un dire che questo potrebbe essere qualcosa di meglio lasciato solo.

+2

NON è DEFINITO in C#, è strettamente definito. Vedi sopra. –

+2

Forse qualcuno dovrebbe andare ad aggiornare la documentazione MSDN, quindi? –

+1

Santo cielo, quella pagina è CHOCK COMPLETA di errori. Parlerò immediatamente con il responsabile della documentazione. Grazie per averlo portato alla mia attenzione –

-1

indice in C# è un tipo di valore, che significa che si restituisce una nuova istanza del valore quando si eseguono operazioni su di esso.

Se si immagina come una procedura al posto di un operatore, la procedura sarebbe simile a questa:

public int Increment(int value) 
{ 
    int returnValue=value+1; 
    return returnValue; 
} 

C++, tuttavia, lavora sul riferimento dell'oggetto, quindi la procedura sarà simile:

Nota: se si stesse applicando l'operatore su un oggetto (ad esempio sovraccarico l'operatore ++), C# si comporterebbe come C++, poiché i tipi di oggetto vengono passati come riferimenti.

+0

Stai dicendo che l'indice C# ++ non modifica l'indice? – Henrik

+0

No, ero solo privato del sonno mentre stavo cercando di spiegare e non pensando giusto –

Problemi correlati