Compilatori e ordine delle argomentazioni di valutazione in C ++

Ok, sono a conoscenza del fatto che lo standard impone che un’implementazione C ++ possa scegliere in quali argomenti di ordine di una funzione vengano valutati, ma ci sono implementazioni che effettivamente ne “approfittano” in uno scenario in cui effettivamente influenzerebbe il programma?

Esempio classico:

int i = 0; foo(i++, i++); 

Nota: non cerco qualcuno che mi dica che l’ordine di valutazione non può essere invocato, ne sono a conoscenza. Mi interessa solo sapere se alcuni compilatori effettivamente valutino da un ordine da sinistra a destra, perché la mia ipotesi sarebbe che se facessero un sacco di codice scritto male si romperebbe (giustamente, ma probabilmente si lamenterebbero ancora).

Dipende dal tipo di argomento, dalla convenzione di chiamata della funzione chiamata, dall’architettura e dal compilatore. Su un x86, la convenzione di chiamata Pascal valuta gli argomenti da sinistra a destra mentre nella convenzione di chiamata C ( __cdecl ) è da destra a sinistra. La maggior parte dei programmi eseguiti su piattaforms multiple tiene conto delle convenzioni di chiamata per evitare sorprese.

C’è un bell’articolo sul blog di Raymond Chen, se sei interessato. Puoi anche dare un’occhiata alla sezione Stack and Calling del manuale GCC.

Edit: Finché stiamo dividendo i capelli: La mia risposta considera questo non come una domanda di lingua ma come una piattaforma. Lo standard linguistico non garantisce o preferisce uno rispetto all’altro e lo lascia come non specificato . Nota la formulazione. Non dice che questo non è definito. Non specificato in questo senso significa qualcosa su cui non puoi contare, comportamento non portatile. Non ho la specifica C / bozza a portata di mano ma dovrebbe essere simile a quella della mia bozza n2798 (C ++)

Alcuni altri aspetti e operazioni della macchina astratta sono descritti in questo Standard Internazionale come non specificato (ad esempio, l’ordine di valutazione di argomenti per una funzione). Ove ansible, questo standard internazionale definisce un insieme di comportamenti consentiti. Questi definiscono gli aspetti non deterministici della macchina astratta. Un’istanza della macchina astratta può quindi avere più di una ansible sequenza di esecuzione per un dato programma e un dato input.

Ho trovato risposta in standard c ++ .

Paragrafo 5.2.2.8:

L’ordine di valutazione degli argomenti non è specificato. Tutti gli effetti collaterali delle valutazioni delle espressioni degli argomenti hanno effetto prima che la funzione venga inserita. L’ordine di valutazione dell’espressione postfissa e dell’elenco di espressioni dell’argomento non è specificato.

In altre parole, dipende solo dal compilatore.

Leggi questo

Non è una copia esatta della tua domanda, ma la mia risposta (e alcuni altri) copre anche la tua domanda.

Ci sono ottime ragioni di ottimizzazione per cui il compilatore potrebbe non scegliere solo da destra a sinistra, ma anche interfogli.

Lo standard non garantisce nemmeno un ordinamento sequenziale. Garantisce solo che quando viene chiamata la funzione, tutti gli argomenti sono stati valutati completamente.

E sì, ho visto alcune versioni di GCC fare esattamente questo. Per il tuo esempio, sarebbe stato chiamato foo (0,0), e sarei 2 successivamente. (Non posso darti il ​​numero esatto della versione del compilatore, era un po ‘di tempo fa, ma non sarei sorpreso di vedere questo comportamento saltar fuori di nuovo: è un modo efficace per programmare le istruzioni)

Tutti gli argomenti sono valutati. Ordine non definito (come da standard). Ma tutte le implementazioni di C / C ++ (che io sappia) valutano gli argomenti della funzione da destra a sinistra . EDIT: CLang è un’eccezione (vedi commento sotto).

Credo che l’ordine di valutazione da destra a sinistra sia stato molto vecchio (dai primi compilatori C). Certamente è stato inventato il C ++ e la maggior parte delle implementazioni di C ++ manterrebbero lo stesso ordine di valutazione perché le prime implementazioni di C ++ venivano semplicemente tradotte in C.

Ci sono alcuni motivi tecnici per valutare gli argomenti della funzione da destra a sinistra. Nelle architetture stack, gli argomenti vengono generalmente inseriti nello stack. In C / C ++, puoi chiamare una funzione con più argomenti di quelli effettivamente specificati – gli argomenti extra vengono semplicemente ignorati. Se gli argomenti vengono valutati da sinistra a destra e spostati da sinistra a destra, lo slot di stack subito sotto il puntatore dello stack manterrà l’ultimo argomento e non è ansible per la funzione ottenere l’offset di un particolare argomento (perché il numero effettivo di argomenti spinti dipende dal chiamante).

In un ordine push da destra a sinistra, lo stack stack proprio sotto il puntatore stack terrà sempre il primo argomento, e lo slot successivo mantiene il secondo argomento, ecc. Gli offset degli argomenti saranno sempre deterministici per la funzione (che può essere scritta e compilato altrove in una libreria, separatamente da dove viene chiamato).

Ora, l’ordine push da destra a sinistra non richiede un ordine di valutazione da destra a sinistra, ma nei primi compilatori la memoria è scarsa. Nell’ordine di valutazione da destra a sinistra, lo stesso stack può essere utilizzato sul posto (in sostanza, dopo aver valutato l’argomento – che può essere un’espressione o una chiamata funciton!), Il valore di ritorno è già nella posizione corretta sul pila). Nella valutazione da sinistra a destra, i valori degli argomenti devono essere memorizzati separatamente e reinseriti nello stack in ordine inverso.

Mi aspetto che i compilatori più moderni cerchino di intercalare le istruzioni che calcolano gli argomenti, dato che sono richiesti dallo standard C ++ per essere indipendenti e quindi privi di qualsiasi interdipendenza. Fare ciò dovrebbe aiutare a mantenere piene le unità di esecuzione della CPU profondamente pipeline e quindi aumentare il throughput. (Almeno mi aspetterei che un compilatore che pretende di essere un compilatore di ottimizzazione lo farebbe quando vengono forniti i flag di ottimizzazione.)

L’ultima volta che ho visto le differenze era tra VS2005 e GCC 3.x su un hardware x86 nel 2007. Quindi è (era?) Una situazione molto probabile. Quindi non faccio mai più affidamento sull’ordine di valutazione. Forse è meglio ora.