2010-05-28 13 views
6

Il codice illustra questa differenza:La precedenza dell'operatore in C++ differisce per i puntatori e gli iteratori?

#include <iostream> 
#include <string> 

int main() 
{ 
     char s[] = "ABCD"; 
     std::string str(s); 

     char *p = s; 
     while(*p) { 
       *p++ = tolower(*p);   // <-- incr after assignment 
     } 
     std::cout << s << std::endl; 

     std::string::iterator it = str.begin(), end = str.end(); 
     while(it != end) { 
       *it++ = tolower(*it);  // <-- incr before assignment ? 
     } 
     std::cout << str << std::endl; 

     return 0; 
} 

produce output:

abcd 
bcd 

se separiamo operazione di assegnazione ed esercente minimo:

while(it != end) { 
    *it = tolower(*it);  // <-- incr before assignment ? 
    it++; 
} 

l'uscita sarà come previsto.

Cosa c'è che non va nel codice originale?

$ g++ --version 
g++ (GCC) 3.4.4 (cygming special, gdc 0.12, using dmd 0.125) 
Copyright (C) 2004 Free Software Foundation, Inc. 

risposta

9

Il problema è che l'ordine di valutazione degli argomenti delle operator= non è specificato. Questo è secondo lo standard C++ 5.2.2/8. Considerare quanto segue:

*it++ = tolower(*it); 

è uguale

operator=(*it++, tolower(*it)); 

Ora *it++ potrebbe essere calcolato prima tolower(*it) e viceversa.

+1

È peggio di un comportamento non specificato, c'è un comportamento non definito nell'espressione originale. Esiste un possibile ordinamento di sottoespressioni in cui 'it ++' viene valutato (cioè una scrittura su 'it') prima che' it' venga letto nella valutazione dei parametri per 'tolower' senza punti di sequenza intermedi. Ciò può accadere indipendentemente dal fatto che l'operatore di assegnazione sia o meno una chiamata di funzione. Ad ogni modo 5 [espr], il paragrafo 8 conferma che si tratta di un comportamento indefinito. –

2
*it++ = tolower(*it); 
*p++ = tolower(*p); 

Entrambe queste linee invocano comportamento indefinito. Non è possibile modificare il valore di una variabile più di una volta in una singola istruzione (++ modifica una volta, operatore = modifica due volte).

Quindi il fatto che si ottengano valori diversi non sorprende.

+0

'operator ++' e 'operator =' applicati qui a due variabili diverse in ciascun caso. Non c'è UB. –

+0

@Kirill V. Lyadvinsky: C'è un comportamento indefinito sia per il puntatore che per il caso iteratore in quanto vi è un possibile sequenziamento delle operazioni in cui non vi è alcun punto intermedio tra la lettura del valore di 'it' (o' p') per l'operatore '*' sul RHS del compito e l'operazione di incremento sul LHS del compito. –

+0

In ogni caso, le linee sono sicuramente confuse e non vorrei vederlo scritto in quel modo comunque :-) –

2

La grammatica funziona esattamente allo stesso modo per i puntatori e gli iteratori. Le operazioni implicite dagli operatori vengono trasformate in chiamate di funzione per oggetti di tipo classe (come la maggior parte degli iteratori).

Il problema con il codice non è con la precedenza degli operatori, tuttavia, in entrambe queste righe non esiste alcuna sequenza tra l'operazione di incremento e la seconda lettura della stessa variabile che viene incrementata altrove nell'istruzione. Per questo motivo, hai un comportamento non definito in modo da poter vedere qualsiasi comportamento dal tuo programma compresi i risultati che stai vedendo.

*p++ = tolower(*p); 

*it++ = tolower(*it); 

È necessario riformulare questa dichiarazione in un modo in cui è definita la sequenza. Immagino che tu voglia qualcosa come questo.

char c = tolower(*p); 
*p++ = c; 
+0

+1: il tuo addon funziona, ma ho un vago modo di capire perché –

+0

Razionale per downvote, chiunque? –

+0

+1 per un buon suggerimento. Potrebbe essere un suggerimento sbagliato alla fine, ma è utile. Prendere in considerazione l'aggiunta di collegamenti allo standard. –