19

ich auf die folgende Situation vor kurzem lief:Auswertung der Reihenfolge der Funktionsargumente und Standardargumente

#include <iostream> 

int *p = 0; 

int f() { 
    p = new int(10); 
    return 0; 
} 

void g(int x, int *y = p) { 
    std::cout << y << std::endl; 
} 

int main() { 
    g(f()); 
} 

Dies ist sehr subtil, da man in der Regel nicht erwarten, die Standardargumente während ihrer Auswertung für den Funktionsaufruf ändern . Ich musste mir die Versammlung ansehen, um diesen Fehler zu erkennen.

Jetzt ist meine Frage: Ist das wirklich undefiniertes Verhalten, da es keine Garantien bezüglich der Bewertungsreihenfolge von Funktionsargumenten gibt?

Antwort

15

Die Reihenfolge der Auswertung (d. H. Bestimmung des Werts) von Funktionsargumenten ist nicht spezifiziert. Der Compiler kann sie in beliebiger Reihenfolge ausführen und sogar mischen, wenn keine anderen Faktoren ihn davon abhalten.

Die Auswertung der Standardargumente erfolgt im Kontext des Aufrufers, nicht des Aufrufers. Also ist der Aufruf von f() für ein Argument notwendig und das Lesen der globalen Variable p für das andere. In welcher Reihenfolge dies geschieht, ist nicht spezifiziert, so dass das globale vor oder nach dem Aufruf von f() gelesen werden kann.

+0

"Die Auswertung der Standardargumente erfolgt im Kontext des Aufrufers" Haben Sie eine Referenz im aktuellen C++ - Standard für diesen Anspruch? – phlipsy

+1

Es gibt eine nicht-normative Anmerkung in 1.9p11, die es ausdrücklich sagt. Abgesehen davon denke ich, dass dies nur durch mangelnde Unterscheidung von anderen Argumenten impliziert wird. –

+2

@phlipsy siehe [dcl.fct.default]/9 "Standardargumente werden jedes Mal ausgewertet, wenn die Funktion aufgerufen wird. Die Reihenfolge der Auswertung der Funktion Argumente ist nicht spezifiziert.", Ich denke, das bedeutet eindeutig, dass 'g (f()) 'ist das gleiche wie' g (f(), p) ' –

13

Wenn ich es richtig verstehe, Ihren Anruf

g(f()); 

entspricht

g(f(), p); 

aufgrund der Erklärung

void g(int x, int *y = p); 

und Argumente an die g Funktion, f() und p , kann in beliebiger Reihenfolge ausgewertet werden, damit Sie 01 bekommen könnenaufgerufen mit y entweder Null zugewiesen (wenn zuerst ausgewertet wird, dann gibt es seinen Anfangswert zurück) oder der neu zugewiesene Arrayzeiger (wenn f() zuerst ausgewertet wird und als Nebeneffekt einen neuen Wert zuweist).