C'è una differenza di prestazioni tra i ++ e ++ i in C?


Risposte:


397

Riepilogo esecutivo: No.

i++potrebbe potenzialmente essere più lento di ++i, poiché il vecchio valore di i potrebbe aver bisogno di essere salvato per un uso successivo, ma in pratica tutti i compilatori moderni lo ottimizzeranno via.

Possiamo dimostrarlo guardando il codice per questa funzione, sia con ++iche i++.

$ cat i++.c
extern void g(int i);
void f()
{
    int i;

    for (i = 0; i < 100; i++)
        g(i);

}

I file sono gli stessi, ad eccezione di ++ie i++:

$ diff i++.c ++i.c
6c6
<     for (i = 0; i < 100; i++)
---
>     for (i = 0; i < 100; ++i)

Li compileremo e otterremo anche l'assemblatore generato:

$ gcc -c i++.c ++i.c
$ gcc -S i++.c ++i.c

E possiamo vedere che sia l'oggetto generato che i file assembler sono gli stessi.

$ md5 i++.s ++i.s
MD5 (i++.s) = 90f620dda862cd0205cd5db1f2c8c06e
MD5 (++i.s) = 90f620dda862cd0205cd5db1f2c8c06e

$ md5 *.o
MD5 (++i.o) = dd3ef1408d3a9e4287facccec53f7d22
MD5 (i++.o) = dd3ef1408d3a9e4287facccec53f7d22

9
So che questa domanda riguarda C, ma sarei interessato a sapere se i browser possono eseguire questa ottimizzazione per javascript.
TM.

69
Quindi il "No" è vero per il compilatore con cui hai provato.
Andreas,

173
@Andreas: bella domanda. Ero un autore di compilatori e ho avuto l'opportunità di testare questo codice su molte CPU, sistemi operativi e compilatori. L'unico compilatore che ho scoperto che non ha ottimizzato il caso i ++ (in realtà, il compilatore che ha portato questo alla mia attenzione in modo professionale) è stato il compilatore Software Toolworks C80 di Walt Bilofsky. Quel compilatore era per sistemi Intel 8080 CP / M. È sicuro dire che qualsiasi compilatore che non include questa ottimizzazione non è pensato per un uso generale.
Mark Harrison,

22
Anche se la differenza di prestazioni è trascurabile e ottimizzata in molti casi, tieni presente che è comunque consigliabile utilizzare al ++iposto di i++. Non c'è assolutamente alcun motivo per non farlo, e se il tuo software passa mai attraverso una toolchain che non lo ottimizza, il tuo software sarà più efficiente. Considerando che è tanto facile da scrivere ++iquanto da digitare i++, in realtà non ci sono scuse per non usarlo ++iin primo luogo.
Monokrome,

7
@monokrome Dato che gli sviluppatori possono fornire la propria implementazione per gli operatori prefisso e postfisso in molte lingue, questa potrebbe non essere sempre una soluzione accettabile per un compilatore senza prima confrontare quelle funzioni, che possono essere non banali.
pickypg

112

Da Efficienza contro l'intenzione da Andrew Koenig:

Innanzitutto, è tutt'altro che ovvio che ++iè più efficiente di i++, almeno per quanto riguarda le variabili intere.

E :

Quindi la domanda che ci si dovrebbe porre non è quale di queste due operazioni sia più veloce, è quale di queste due operazioni esprime più accuratamente ciò che si sta tentando di realizzare. Sottolineo che se non si utilizza il valore dell'espressione, non c'è mai un motivo da usare i++invece di ++i, perché non c'è mai un motivo per copiare il valore di una variabile, incrementare la variabile e quindi buttare via la copia.

Quindi, se il valore risultante non viene utilizzato, lo userei ++i. Ma non perché sia ​​più efficiente: perché afferma correttamente il mio intento.


5
Non dimentichiamo che anche altri operatori unari sono prefissi. Penso che ++ i sia il modo "semantico" di usare un operatore unario, mentre i ++ è in giro per soddisfare un'esigenza specifica (valutazione prima dell'aggiunta).
TM.

9
Se il valore risultante non viene utilizzato, non vi è alcuna differenza nella semantica: vale a dire, non esiste una base per preferire l'uno o l'altro costrutto.
Eamon Nerbonne,

3
Come lettore, vedo la differenza. Quindi come scrittore, mostrerò il mio intento scegliendo l'uno sull'altro. Ho solo l'abitudine di provare a comunicare con i miei amici programmatori e compagni di squadra tramite il codice :)
Sébastien RoccaSerra,

11
Seguendo il consiglio di Koenig, scrivo i++nello stesso modo in cui codifico i += noi = i + n , cioè, sotto forma di destinazione verb oggetto , con l' obiettivo di operando a sinistra del verbo dell'operatore. Nel caso di i++, non esiste un oggetto giusto , ma la regola si applica comunque, mantenendo l' obiettivo a sinistra dell'operatore verbo .
David R Tribble,

4
Se stai cercando di incrementare i, allora i ++ e ++ i esprimono entrambi correttamente il tuo intento. Non c'è motivo di preferire l'uno all'altro. Come lettore non vedo alcuna differenza, i tuoi colleghi si confondono quando vedono i ++ e pensano, forse questo è un errore di battitura e non intendeva incrementare i?
Dan Carter,

46

Una risposta migliore è che ++ia volte sarà più veloce ma mai più lenta.

Tutti sembrano presumere che si itratti di un normale tipo incorporato come int. In questo caso non ci saranno differenze misurabili.

Tuttavia, se iè di tipo complesso, potresti trovare una differenza misurabile. Perché i++devi fare una copia della tua classe prima di incrementarla. A seconda di ciò che è coinvolto in una copia, potrebbe effettivamente essere più lento poiché con ++itte puoi semplicemente restituire il valore finale.

Foo Foo::operator++()
{
  Foo oldFoo = *this; // copy existing value - could be slow
  // yadda yadda, do increment
  return oldFoo;
}

Un'altra differenza è che con ++ite hai la possibilità di restituire un riferimento anziché un valore. Ancora una volta, a seconda di ciò che è coinvolto nella creazione di una copia del tuo oggetto, questo potrebbe essere più lento.

Un esempio del mondo reale di dove ciò può accadere sarebbe l'uso di iteratori. È improbabile che la copia di un iteratore sia un collo di bottiglia nella tua applicazione, ma è comunque buona prassi prendere l'abitudine di utilizzare ++iinvece di i++dove il risultato non è influenzato.


36
La domanda afferma esplicitamente C, nessun riferimento a C ++.
Dan Cristoloveanu,

5
Questa domanda (dichiaratamente vecchia) riguardava il C, non il C ++, ma immagino che valga anche la pena ricordare che, in C ++, anche se una classe implementa operatori post e pre-fix, non sono nemmeno necessariamente correlati. Ad esempio, la barra ++ può incrementare un membro di dati, mentre la barra ++ può incrementare un membro di dati diverso e, in questo caso, non avresti la possibilità di utilizzare nessuno dei due, poiché la semantica è diversa.
Kevin

-1 Anche se questo è un buon consiglio per i programmatori C ++, non risponde alla domanda, che è etichettata C, per lo meno. In C, non fa assolutamente alcuna differenza se usi il prefisso o il postfisso.
Lundin,

@Pacerier La domanda è contrassegnata solo con C e C. Perché pensi che non siano interessati a questo? Dato come funziona SO, non sarebbe piuttosto intelligente supporre che siano interessati solo a C, piuttosto che a Java, C #, COBOL o qualsiasi altro linguaggio off-topic?
Lundin,

18

Risposta breve:

Non c'è mai alcuna differenza tra i++e ++iin termini di velocità. Un buon compilatore non dovrebbe generare codice diverso nei due casi.

Risposta lunga:

Ciò che ogni altra risposta non menziona è che la differenza tra ++iversus i++ha senso solo nell'espressione che trova.

In caso di for(i=0; i<n; i++) , the i++è solo nella sua stessa espressione: c'è un punto di sequenza prima di i++e c'è uno dopo di esso. Pertanto, l'unico codice macchina generato è "aumenta idi 1" ed è ben definito il modo in cui questo è sequenziato rispetto al resto del programma. Quindi, se lo cambiassi in prefisso ++, non importerebbe minimamente, otterresti comunque il codice della macchina "aumenta idi 1".

Le differenze tra ++ie i++contano solo in espressioni come array[i++] = x;contro array[++i] = x;. Alcuni potrebbero obiettare e dire che il postfix sarà più lento in tali operazioni perché il registro in cui irisiede deve essere ricaricato in seguito. Ma poi nota che il compilatore è libero di ordinare le tue istruzioni nel modo che preferisce, purché non "rompa il comportamento della macchina astratta" come lo chiama lo standard C.

Quindi, mentre puoi supporre che array[i++] = x;venga tradotto in codice macchina come:

  • Memorizza il valore inel registro A.
  • Memorizzare l'indirizzo dell'array nel registro B.
  • Aggiungi A e B, archivia i risultati in A.
  • A questo nuovo indirizzo rappresentato da A, memorizzare il valore di x.
  • Valore del negozio di i nel registro A // inefficiente perché istruzioni aggiuntive qui, l'abbiamo già fatto una volta.
  • Registro degli incrementi A.
  • Memorizza registro A in i.

il compilatore potrebbe anche produrre il codice in modo più efficiente, come ad esempio:

  • Memorizza il valore inel registro A.
  • Memorizzare l'indirizzo dell'array nel registro B.
  • Aggiungi A e B, archivia i risultati in B.
  • Registro degli incrementi A.
  • Memorizza registro A in i.
  • ... // resto del codice.

Solo perché tu come programmatore C sei addestrato a pensare che il postfix ++avvenga alla fine, il codice macchina non deve essere ordinato in quel modo.

Quindi non c'è differenza tra prefisso e postfisso ++ in C. Ora, ciò che si dovrebbe variare da programmatore C è che le persone che usano in modo incoerente il prefisso in alcuni casi e il postfisso in altri casi, senza alcun motivo logico. Ciò suggerisce che non sono sicuri su come funzioni la C o che abbiano una conoscenza errata della lingua. Questo è sempre un brutto segno, a sua volta suggerisce che stanno prendendo altre decisioni discutibili nel loro programma, basate su superstizioni o "dogmi religiosi".

"Il prefisso ++è sempre più veloce" è in effetti uno di questi falsi dogmi che è comune tra gli aspiranti programmatori C.


3
Nessuno ha detto "Prefisso ++ è sempre più veloce". È quotato male. Quello che hanno detto è "Postfix ++ non è mai più veloce".
Pacerier,

2
@Pacerier Non sto citando una persona in particolare, ma solo una convinzione diffusa e errata.
Lundin,

@rbaleksandar C ++! = C.
Lundin

@rbaleksandar La domanda e la risposta parlano entrambe di C ++. Che è diverso da C, poiché ha un sovraccarico dell'operatore e costruttori di copie, ecc.
Lundin,

3
@rbaleksandar c ++ == c && ++ c! = c
sadljkfhalskdjfh

17

Prendendo una foglia da Scott Meyers, Più efficace c ++ Voce 6: Distinguere tra prefisso e postfisso forme di operazioni di incremento e decremento .

La versione del prefisso è sempre preferita rispetto al postfisso per quanto riguarda gli oggetti, specialmente per quanto riguarda gli iteratori.

Il motivo di ciò se si osserva il modello di chiamata degli operatori.

// Prefix
Integer& Integer::operator++()
{
    *this += 1;
    return *this;
}

// Postfix
const Integer Integer::operator++(int)
{
    Integer oldValue = *this;
    ++(*this);
    return oldValue;
}

Guardando questo esempio è facile vedere come l'operatore prefisso sarà sempre più efficiente del postfisso. A causa della necessità di un oggetto temporaneo nell'uso del postfix.

Questo è il motivo per cui quando vedi esempi che usano iteratori usano sempre la versione del prefisso.

Ma come fai notare per int non c'è effettivamente alcuna differenza a causa dell'ottimizzazione del compilatore che può avere luogo.


4
Penso che la sua domanda fosse rivolta al C, ma per il C ++ hai assolutamente ragione, e inoltre le persone del C dovrebbero adottarlo perché possono usarlo anche per il C ++. Troppo spesso vedo i programmatori C usare la sintassi postfix ;-)
Anders Rune Jensen,

-1 Anche se questo è un buon consiglio per i programmatori C ++, non risponde alla domanda, che è etichettata C, per lo meno. In C, non fa assolutamente alcuna differenza se usi il prefisso o il postfisso.
Lundin,

1
Il prefisso @Lundin e i postifx contano in C secondo la risposta di Andreas . Non puoi presumere che il compilatore si ottimizzerà, ed è una buona pratica per qualsiasi lingua preferire Alwas ++ i rispetto a i ++
JProgrammer

@JProgrammer Non contano secondo la tua risposta sinceramente :) Se scopri che producono un codice diverso, ciò è dovuto al fatto che non hai abilitato le ottimizzazioni o che il compilatore non è valido. Ad ogni modo, la tua risposta è fuori tema poiché la domanda riguardava C.
Lundin,

16

Ecco un'ulteriore osservazione se sei preoccupato per la micro ottimizzazione. I cicli decrescenti possono "possibilmente" essere più efficienti dei circuiti incrementali (a seconda dell'architettura del set di istruzioni, ad esempio ARM), dato che:

for (i = 0; i < 100; i++)

Su ogni ciclo avrai un'istruzione ciascuno per:

  1. Aggiunta 1a i.
  2. Confronta se iè inferiore a 100.
  3. Un ramo condizionale se iè inferiore a 100.

Considerando che un ciclo in decremento:

for (i = 100; i != 0; i--)

Il loop avrà un'istruzione per ciascuno di:

  1. Decrementa i, impostando il flag di stato del registro CPU.
  2. Un ramo condizionale a seconda dello stato del registro CPU ( Z==0).

Ovviamente questo funziona solo quando si decrementa a zero!

Ricordato dalla Guida per gli sviluppatori del sistema ARM.


Buona Ma questo non crea meno hit nella cache?
AndreasT

C'è un vecchio strano trucco dai libri di ottimizzazione del codice, che combina il vantaggio del ramo zero-test con l'indirizzo incrementato. Ecco un esempio in un linguaggio di alto livello (probabilmente inutile, poiché molti compilatori sono abbastanza intelligenti da sostituirlo con un codice di loop meno efficiente ma più comune): int a [N]; per (i = -N; i; ++ i) a [N + i] + = 123;
noop,

@noop potresti forse approfondire su quali libri di ottimizzazione del codice ti riferisci? Ho faticato a trovarne di buoni.
Mezamorphic

7
Questa risposta non è affatto una risposta alla domanda.
Monokrome,

@mezamorphic Per x86 le mie fonti sono: manuali di ottimizzazione Intel, Agner Fog, Paul Hsieh, Michael Abrash.
Noop

11

Si prega di non lasciare che la questione di "quale sia più veloce" sia il fattore decisivo di quale utilizzare. È probabile che non ti interesserai mai così tanto, e inoltre, il tempo di lettura del programmatore è molto più costoso del tempo della macchina.

Usa quello che ha più senso per l'essere umano che legge il codice.


3
Credo che sia sbagliato preferire vaghi miglioramenti della leggibilità ai guadagni effettivi di efficienza e alla chiarezza generale delle intenzioni.
noop,

4
Il mio termine "tempo di lettura del programmatore" è approssimativamente analogo a "chiarezza di intenti". "I guadagni effettivi di efficienza" sono spesso incommensurabili, abbastanza vicini allo zero da chiamarli zero. Nel caso del PO, a meno che il codice non sia stato profilato per scoprire che ++ i è un collo di bottiglia, la domanda su quale sia più veloce è una perdita di tempo e unità di pensiero del programmatore.
Andy Lester,

2
La differenza nella leggibilità tra ++ i e i ++ è solo una questione di preferenza personale, ma ++ i implica chiaramente un'operazione più semplice di i ++, nonostante il risultato sia equivalente per casi banali e tipi di dati semplici quando si tratta di ottimizzare il compilatore. Pertanto ++ i è un vincitore per me, quando non sono necessarie proprietà specifiche di post-incremento.
noop,

Stai dicendo quello che sto dicendo. È più importante mostrare l'intento e migliorare la leggibilità piuttosto che preoccuparsi dell '"efficienza".
Andy Lester,

2
Non posso ancora essere d'accordo. Se la leggibilità è in cima all'elenco delle priorità, forse la scelta del linguaggio di programmazione è errata. Lo scopo principale di C / C ++ è scrivere codice efficiente.
Noop

11

Prima di tutto: la differenza tra i++ed ++iè trascurabile in C.


Ai dettagli.

1. Il noto problema C ++: ++iè più veloce

In C ++, ++iè più efficiente iff iè una sorta di oggetto con un operatore di incremento sovraccarico.

Perché?
In ++i, l'oggetto viene prima incrementato e successivamente può essere passato come riferimento const a qualsiasi altra funzione. Questo non è possibile se l'espressione è foo(i++)perché ora è necessario eseguire l'incremento prima di foo()chiamare, ma è necessario passare il vecchio valore foo(). Di conseguenza, il compilatore è costretto a fare una copia iprima di eseguire l'operatore di incremento sull'originale. Le chiamate aggiuntive di costruttore / distruttore sono la parte negativa.

Come notato sopra, questo non si applica ai tipi fondamentali.

2. Il fatto poco noto: i++ può essere più veloce

Se non è necessario chiamare alcun costruttore / distruttore, come sempre in C, ++ie i++dovrebbe essere altrettanto veloce, giusto? No. Sono praticamente ugualmente veloci, ma potrebbero esserci piccole differenze, che la maggior parte degli altri risponditori ha sbagliato.

Come può i++essere più veloce?
Il punto sono le dipendenze dei dati. Se il valore deve essere caricato dalla memoria, due operazioni successive devono essere eseguite con esso, incrementandolo e utilizzandolo. Con ++i, è necessario eseguire l'incremento prima di poter utilizzare il valore. Con i++, l'uso non dipende dall'incremento e la CPU può eseguire l'operazione d'uso parallelamente all'operazione di incremento. La differenza è al massimo un ciclo della CPU, quindi è davvero trascurabile, ma è lì. Ed è il contrario, quindi molti si aspetterebbero.


Informazioni sul punto 2: Se ++io i++viene utilizzato all'interno di un'altra espressione, la modifica tra di esse modifica la semantica dell'espressione, quindi ogni possibile guadagno / perdita di prestazione è fuori discussione. Se sono autonomi, ovvero il risultato dell'operazione non viene utilizzato immediatamente, quindi qualsiasi compilatore decente lo compilerebbe nella stessa cosa, ad esempio un'istruzione di INCassemblaggio.
Shahbaz,

1
@Shahbaz È assolutamente vero, ma non è questo il punto. 1) Anche se la semantica è diversa, entrambe i++e ++ipossono essere utilizzate in modo intercambiabile in quasi tutte le situazioni possibili regolando le costanti del loop di una, quindi sono quasi equivalenti in ciò che fanno per il programmatore. 2) Anche se entrambi vengono compilati con la stessa istruzione, la loro esecuzione differisce per la CPU. Nel caso di i++, la CPU può calcolare l'incremento in parallelo ad alcune altre istruzioni che usano lo stesso valore (le CPU lo fanno davvero!), Mentre con ++ila CPU deve programmare le altre istruzioni dopo l'incremento.
cmaster - ripristina monica il

4
@Shahbaz Ad esempio: if(++foo == 7) bar();e if(foo++ == 6) bar();sono funzionalmente equivalenti. Tuttavia, il secondo può essere più veloce di un ciclo, poiché il confronto e l'incremento possono essere calcolati in parallelo dalla CPU. Non che questo singolo ciclo sia molto importante, ma la differenza è lì.
cmaster - ripristina monica il

1
Buon punto. Le costanti si presentano molto (oltre che <per esempio contro <=) dove ++vengono di solito utilizzate, quindi la conversione tra i sebbene è spesso facilmente possibile.
Shahbaz,

1
Mi piace il punto 2, ma questo vale solo se si utilizza il valore, giusto? La domanda dice "se il valore risultante non viene utilizzato?", Quindi può essere fonte di confusione.
jinawee,

7

@Mark Anche se al compilatore è permesso ottimizzare la copia temporanea (basata sullo stack) della variabile e gcc (nelle ultime versioni) lo fa, non significa che tutti i compilatori lo faranno sempre.

L'ho appena testato con i compilatori che utilizziamo nel nostro progetto attuale e 3 su 4 non lo ottimizzano.

Non dare mai per scontato che il compilatore funzioni correttamente, specialmente se il codice forse più veloce, ma mai più lento, è facile da leggere.

Se non hai un'implementazione davvero stupida di uno degli operatori nel tuo codice:

Preferisco sempre ++ i rispetto a i ++.


Solo curioso ... perché usi 4 diversi compilatori C in un progetto? O in una squadra o in una società, del resto?
Lawrence Dol,

3
Quando si creano giochi per console ogni piattaforma porta il proprio compilatore / toolchain. In un mondo perfetto potremmo usare gcc / clang / llvm per tutti gli obiettivi, ma in questo mondo dobbiamo sopportare Microsoft, Intel, Metroworks, Sony, ecc.
Andreas,

5

In C, il compilatore può generalmente ottimizzarli affinché siano gli stessi se il risultato non viene utilizzato.

Tuttavia, in C ++ se si utilizzano altri tipi che forniscono i propri operatori ++, è probabile che la versione del prefisso sia più veloce della versione postfix. Quindi, se non hai bisogno della semantica postfix, è meglio usare l'operatore prefisso.


4

Mi viene in mente una situazione in cui Postfix è più lento dell'incremento del prefisso:

Immagina un processore con registro A sia usato come accumulatore ed è l'unico registro usato in molte istruzioni (alcuni piccoli microcontrollori sono in realtà così).

Ora immagina il seguente programma e la loro traduzione in un'ipotetica assemblea:

Incremento prefisso:

a = ++b + c;

; increment b
LD    A, [&b]
INC   A
ST    A, [&b]

; add with c
ADD   A, [&c]

; store in a
ST    A, [&a]

Incremento Postfix:

a = b++ + c;

; load b
LD    A, [&b]

; add with c
ADD   A, [&c]

; store in a
ST    A, [&a]

; increment b
LD    A, [&b]
INC   A
ST    A, [&b]

Nota come il valore di b stato forzato il ricaricamento . Con l'incremento del prefisso, il compilatore può semplicemente incrementare il valore e continuare ad usarlo, possibilmente evitare di ricaricarlo poiché il valore desiderato è già nel registro dopo l'incremento. Tuttavia, con l'incremento postfix, il compilatore deve fare i conti con due valori, uno il vecchio e uno il valore incrementato che, come ho mostrato sopra, comporta un ulteriore accesso alla memoria.

Naturalmente, se il valore dell'incremento non viene utilizzato, come una singola i++;istruzione, il compilatore può (e non fa) semplicemente generare un'istruzione di incremento indipendentemente dall'uso di postfisso o prefisso.


Come nota a margine, vorrei menzionare che un'espressione in cui esiste un b++non può essere semplicemente convertita in una ++bsenza alcuno sforzo aggiuntivo (ad esempio aggiungendo a - 1). Quindi confrontare i due se fanno parte di qualche espressione non è realmente valido. Spesso, dove si utilizza b++all'interno di un'espressione che non è possibile utilizzare ++b, quindi anche se ++bfosse potenzialmente più efficiente, sarebbe semplicemente sbagliato. L'eccezione è ovviamente se l'espressione la sta implorando (ad esempio, a = b++ + 1;che può essere cambiata in a = ++b;).


4

Ho letto attraverso la maggior parte delle risposte qui e molti dei commenti, e non ho visto alcun riferimento a un'istanza che ho potuto pensare a dove è più efficiente di (e forse sorprendentemente era più efficiente di ). Questo è per i compilatori C per il DEC PDP-11!i++++i--i i--

Il PDP-11 aveva istruzioni di assemblaggio per il pre-decremento di un registro e post-incremento, ma non viceversa. Le istruzioni consentivano di utilizzare qualsiasi registro "generico" come puntatore di stack. Quindi, se hai usato qualcosa del genere *(i++), potrebbe essere compilato in una singola istruzione di assemblaggio, mentre *(++i)non è possibile.

Questo è ovviamente un esempio molto esoterico, ma fornisce l'eccezione in cui il post-incremento è più efficiente (o dovrei dire lo è , dal momento che non c'è molta richiesta di codice C PDP-11 in questi giorni).


2
Molto esoterico, ma molto interessante!
Mark Harrison,

@daShier. +1, anche se non sono d'accordo, questo non è così esoterico, o almeno non dovrebbe esserlo. C è stato sviluppato in collaborazione con Unix presso AT&T Bell Labs nei primi anni '70, quando il PDP-11 era il processore target. Nel codice sorgente Unix di questo periodo l'incremento post, "i ++", è più diffuso in parte perché gli sviluppatori sapevano quando veniva assegnato il valore, "j = i ++", o usato come indice, "a [i ++] = n", il codice sarebbe leggermente più veloce (e più piccolo). Sembra che abbiano preso l'abitudine di utilizzare l'incremento post a meno che non fosse richiesto pre. Altri hanno imparato leggendo il loro codice e hanno anche preso questa abitudine.
Jimhark,

Il 68000 ha la stessa funzionalità, post-incremento e pre-decremento sono supportati nell'hardware (come le modalità di indirizzamento), ma non viceversa. Il team Motorola è stato ispirato dal DEC PDP-11.
Jimhark,

2
@jimhark, sì, sono uno di quei programmatori PDP-11 che sono passati al 68000 e ancora usano --ie i++.
daShier,

2

Preferisco sempre il pre-incremento, tuttavia ...

Volevo sottolineare che anche nel caso di chiamare la funzione operator ++, il compilatore sarà in grado di ottimizzare il temporaneo se la funzione viene incorporata. Poiché l'operatore ++ è in genere breve e spesso implementato nell'intestazione, è probabile che sia inline.

Quindi, ai fini pratici, probabilmente non c'è molta differenza tra le prestazioni delle due forme. Tuttavia, preferisco sempre il pre-incremento poiché sembra meglio esprimere direttamente ciò che sto cercando di dire, piuttosto che fare affidamento sull'ottimizzatore per capirlo.

Inoltre, dare meno all'optmizer probabilmente significa che il compilatore funziona più velocemente.


La tua pubblicazione è specifica per C ++ mentre la domanda riguarda C. In ogni caso, la tua risposta è sbagliata: per gli operatori di post-incremento personalizzati il ​​compilatore generalmente non sarà in grado di produrre un codice efficiente.
Konrad Rudolph,

Egli afferma "se la funzione viene incorporata" e questo rende corretto il suo ragionamento.
Blaisorblade,

Poiché C non fornisce un sovraccarico da parte dell'operatore, la domanda pre-post è in gran parte poco interessante. Il compilatore può ottimizzare una temperatura inutilizzata utilizzando la stessa logica che viene applicata a qualsiasi altro valore non utilizzato, calcolato in modo primitivo. Vedi la risposta selezionata per un campione.

0

La mia C è un po 'arrugginita, quindi mi scuso in anticipo. Rapidamente, posso capire i risultati. Ma sono confuso su come entrambi i file siano usciti nello stesso hash MD5. Forse un ciclo for funziona allo stesso modo, ma le seguenti 2 righe di codice non genererebbero assembly diversi?

myArray[i++] = "hello";

vs

myArray[++i] = "hello";

Il primo scrive il valore nell'array, quindi incrementa i. I secondi incrementi quindi scrivo sull'array. Non sono un esperto di assemblaggio, ma non vedo come lo stesso eseguibile sarebbe generato da queste 2 diverse righe di codice.

Solo i miei due centesimi.


1
@Jason Z L'ottimizzazione del compilatore avviene prima della generazione dell'assembly, vedrebbe che la variabile i non viene utilizzata in nessun altro posto sulla stessa linea, quindi mantenere il suo valore sarebbe uno spreco, probabilmente lo inoltra effettivamente a i ++. Ma è solo una supposizione. Non vedo l'ora che uno dei miei docenti provi a dire che è più veloce e posso diventare il ragazzo che corregge una teoria con prove pratiche. Posso quasi già sentire l'animosità ^ _ ^
Tarks

3
"La mia C è un po 'arrugginita, quindi mi scuso in anticipo." Nulla di sbagliato con la tua C, ma non hai letto per intero la domanda originale: "Esiste una differenza di prestazioni tra i ++ e ++ i se il valore risultante non viene utilizzato? " Nota il corsivo. Nel tuo esempio, il 'risultato' di i ++ / ++ i è usato, ma nel idiomatica per ciclo, il 'risultato' del pre dell'operatore / post-incremento non viene utilizzato in modo che il compilatore può fare ciò che gli piace.
Roddy,

2
nel tuo esempio il codice sarebbe diverso, perché stai usando il valore. l'esempio in cui erano uguali utilizzava solo ++ per l'incremento e non utilizzava il valore restituito da nessuno dei due.
John Gardner,

1
Correzione: "il compilatore vedrebbe che il valore restituito di i ++ non viene utilizzato, quindi lo capovolgerebbe a ++ i". Quello che hai scritto è sbagliato anche perché non puoi avere i insieme a uno di i ++, i ++ sulla stessa riga (istruzione), quel risultato non è definito.
Blaisorblade,

1
Passare foo[i++]a foo[++i]senza cambiare nient'altro cambierebbe ovviamente la semantica del programma, ma su alcuni processori quando si utilizza un compilatore senza una logica di ottimizzazione di sollevamento del ciclo, l'incremento pe l' qesecuzione di un ciclo che esegue, ad esempio, *(p++)=*(q++);sarebbe più veloce dell'uso di un ciclo che esegue *(++pp)=*(++q);. Per loop molto stretti su alcuni processori, la differenza di velocità può essere significativa (oltre il 10%), ma è probabilmente l'unico caso in C in cui il post-incremento è materialmente più veloce del pre-incremento.
supercat
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.