2016-07-16 33 views
21

Wie weiß der Compiler, wo im Speicher die Quadratwurzel sein wird, bevor das Programm ausgeführt wird? Ich dachte, die andere Adresse jedes Mal würde das Programm ausgeführt wird, aber dies funktioniert:Warum können Funktionszeiger "constexpr" sein?

constexpr double(*fp)(double) = &sqrt; 
cout << fp(5.0); 

Ist es, weil die Adresse an eine andere Adresse im Speicher ist relativ? Ich denke nicht, weil der Wert von fp groß ist: 0x720E1B94.

+0

Der Linkernel tut diesen Job nicht den Compiler. Linker findet heraus, wo sich die Dinge befinden –

+0

Es ist nie die echte Adresse – Arunmu

+0

@EdHeal Das ist immer noch vor der Ausführung, und ich verstehe nicht, warum der Wert für jede Ausführung gleich sein kann ?! – Coolwater

Antwort

5

Wie weiß der Compiler, wo im Speicher die Quadratwurzel sein wird, bevor das Programm ausgeführt wird?

Die Werkzeugkette entscheidet, wo sie die Funktionen ablegt.

Liegt das daran, dass die Adresse relativ zu einer anderen Adresse im Speicher ist?

Wenn das produzierte Programm ist entweder relocatable oder position independent dann ja, das ist der Fall. Wenn das Programm keins ist, kann die Adresse sogar absolut sein.

Warum sollten genau die gleichen Speicherplätze verfügbar sein, wenn das Programm das nächste Mal ausgeführt wird?

Da der Speicherplatz virtual ist.

+2

Nun, der Platz ist virtuell, aber [ASLR] (https://en.wikipedia.org/wiki/Address_space_layout_randomization) wird heute auch häufig verwendet. – Ruslan

22

Zur Kompilierzeit kennt der Compiler die Adresse sqrt nicht. Sie können jedoch nichts zur Kompilierzeit mit einem consExpr-Funktionszeiger ausführen, der es Ihnen ermöglichen würde, auf die Adresse dieses Zeigers zuzugreifen. Daher kann ein Funktionszeiger zum Zeitpunkt der Kompilierung als undurchsichtiger Wert behandelt werden.

Und da Sie eine constexpr-Variable nicht ändern können, nachdem sie initialisiert wurde, kann jeder constexpr-Funktionszeiger auf die Position einer bestimmten Funktion reduziert werden.

Wenn Sie tat etwas wie folgt aus:

using fptr = float(*)(float); 

constexpr fptr get_func(int x) 
{ 
    return x == 3 ? &sqrtf : &sinf; 
} 

constexpr fptr ptr = get_func(12); 

Der Compiler genau erkennen kann, die get_func funktionieren für bestimmte Kompilierung Wert zurück. So get_func(12) reduziert sich auf &sinf. Also was auch immer &sinf kompilieren würde ist genau was get_func(12) kompilieren würde.

+0

* kann * oder * muss *? – Deduplicator

+1

@Deduplicator: beide. –

+0

Das erklärt nicht, wie der Compiler weiß, was der Wert von & Sinf ist. Nur @ hydes Antwort erklärt das. – fishinear

13

Der Adresswert wird von einem Linker zugewiesen, sodass der Compiler den genauen Adresswert nicht kennt.

cout << fp(5.0); 

Dies funktioniert, weil es zur Laufzeit ausgewertet wird, nachdem die genaue Adresse aufgelöst wurde.

Im Allgemeinen können Sie den tatsächlichen Wert (Adresse) von constexpr Zeiger nicht verwenden, da es zur Kompilierzeit nicht bekannt ist.

Bjarne Stroustrup C++ Programmiersprache 4. Auflage erwähnt: Konstante Ausdrücke

Die Adresse eines statisch zugewiesenen Objekt (§6.4.2), wie ein

10.4.5 Adresse globale Variable, ist eine Konstante. Sein Wert wird jedoch vom Linker und nicht vom Compiler zugewiesen, sodass der Compiler den Wert einer solchen Adresskonstanten nicht kennen kann. Dies begrenzt den Bereich der konstanten Ausdrücke von Zeiger und Referenztyp. Zum Beispiel:

constexpr const char∗ p1 = "asdf"; 
    constexpr const char∗ p2 = p1;  // OK 
    constexpr const char∗ p2 = p1+2; // error : the compiler does not know the value of p1 
    constexpr char c = p1[2];   // OK, c==’d’; the compiler knows the value pointed to by p1 
+2

Beachten Sie, dass 'constexpr const char * p3 = p1 + 2;' ist in Ordnung, soweit ich das beurteilen kann. 'p1 + 2' ist ein Wert, der die Adresse eines Unterobjekts mit statischer Speicherdauer darstellt und ein gültiger konstanter Ausdruck ist; In diesem Fall ist kein vollständiges Objekt erforderlich. Sie können es nicht als ein Nicht-Typ-Template-Argument verwenden, aber das ist eine andere Geschichte. Ich kann im Standard nichts finden, was solch ein Konstrukt schlecht aussehen lassen würde; Dies gilt für C++ 11 und höhere Versionen. Alle Compiler, die ich getestet habe, akzeptieren es. Siehe auch [diese Antwort] (http://stackoverflow.com/a/17660391/4326278). – bogdan

3

Es ist ganz einfach.

Überlegen Sie, wie Compiler die Adresse kennt, in diesem Code zu nennen:

puts("hey!"); 

Compiler hat keine Ahnung von der Lage von puts, und es auch nicht zu einem Laufzeitsuche für sie hinzufügen (das wäre eher schlecht für die Leistung, obwohl es ist, was virtuelle Methoden der Klassen tun müssen. Die Möglichkeit, zur Laufzeit eine andere Version der dynamischen Bibliothek zu haben (ganz zu schweigen von der Adressraum-Layout-Randomisierung, auch wenn es sich um exakt dieselbe Bibliotheksdatei handelt), stellt sicher, dass der Toolchain-Linker für die Build-Zeit es auch nicht kennt.

So ist es an der dynamic linker, um die Adresse zu beheben, wenn es das kompilierte Binärprogramm startet. Dies wird Verlagerung genannt.

Exakt gleiche geschieht mit Ihrem constexpr: Compiler jeden Ort im Code mit dieser Adresse in der Tabelle Verlagerung fügt, und dann dynamische Linker macht seinen Job jedes Mal das Programm startet.