Arithmetische Operationen zwischen Konstanten
-
09-12-2019 - |
Frage
Betrachten Sie diesen Code.
#define A 5
#define B 3
int difference = A - B;
Ist der Wert von „Differenz“ in der Kompilierungszeit fest als „2“ codiert oder wird er zur Laufzeit berechnet?
Lösung
Der A
Und B
Makros sind etwas ablenkend.Das:
#define A 5
#define B 3
int difference = A - B;
entspricht genau diesem:
int difference = 5 - 3;
Lassen Sie uns also Letzteres besprechen.
5 - 3
ist ein beständiger Ausdruck, ein Ausdruck, der „während der Übersetzung und nicht zur Laufzeit ausgewertet werden kann und dementsprechend an jeder Stelle verwendet werden kann, an der sich eine Konstante befindet“.Es ist auch ein *ganzzahliger konstanter Ausdruck.Beispielsweise muss eine Fallbezeichnung ein ganzzahliger Konstantenausdruck sein, Sie könnten also Folgendes schreiben:
switch (foo) {
case 2: /* this is a constant */
...
}
oder dieses:
switch (foo) {
case 5 - 3: /* this is a constant expression */
...
}
Beachten Sie jedoch, dass die Definition dies besagt kann sein während der Übersetzung ausgewertet, nicht dass es so sein muss.Es gibt einige Kontexte, die konstante Ausdrücke und in diesen Kontexten den Ausdruck erfordern muss zur Kompilierungszeit ausgewertet werden.
Aber davon ausgehend difference
innerhalb einer Funktion deklariert wird, ist der Initialisierer keiner dieser Kontexte.
Jeder Compiler, der das Geld wert ist, das Sie dafür bezahlen (auch wenn er kostenlos ist), wird reduziert 5 - 3
Zu 2
zur Kompilierungszeit und generieren Sie Code, der den Wert speichert 2
In difference
.Dies ist jedoch nicht erforderlich.Der C-Standard spezifiziert die Verhalten von Programmen;Es wird nicht angegeben, wie dieses Verhalten implementiert werden muss.Man kann jedoch mit Sicherheit davon ausgehen, dass der von Ihnen verwendete Compiler ersetzt wird 5 - 3
von 2
.
Auch wenn Sie schreiben:
int difference = 2;
Ein Compiler könnte legal Code generieren, der den Wert lädt 5
in ein Register, subtrahiert 3
daraus und speichert den Inhalt des Registers darin difference
.Das wäre eine dumme Sache, aber der Sprachstandard schließt es nicht aus.
Solange das Endergebnis das ist difference
hat den Wert 2
, Dem Sprachstandard ist es egal, wie es gemacht wird.
Wenn Sie hingegen schreiben:
switch (foo) {
case 5 - 3: /* ... */
case 2: /* ... */
}
dann der Compiler muss Berechnen Sie das Ergebnis, damit der Fehler diagnostiziert werden kann (Sie können nicht zwei Fallbezeichnungen mit demselben Wert haben.
Schließlich, wenn Sie definieren difference
im Dateibereich (außerhalb einer Funktion), dann der Anfangswert tut müssen konstant sein.Aber der eigentliche Unterschied besteht in diesem Fall nicht darin, ob 5 - 3
Wird zur Kompilierungszeit ausgewertet, es geht darum, ob Sie es sind erlaubt einen nicht konstanten Ausdruck verwenden.
Referenz:Der neueste Entwurf des C-Standards von 2011 ist N1570 (großes PDF);Konstante Ausdrücke werden in Abschnitt 6.6 besprochen.
Andere Tipps
Der Standard spezifiziert so etwas nicht.Es sagt nichts über mögliche Optimierungen wie diese aus (und das aus gutem Grund).Ein Standard definiert die Semantik, nicht die Implementierung.
Warum schauen Sie sich nicht die Demontage Ihres Compilers an?Das wird Ihnen eine definitive Antwort geben.
...
Also lasst uns das tun.
Hier ist die Ausgabe von VC++ 10:
#include <iostream>
#define A 5
#define B 3
int main() {
int x = A - B;
std::cout << x; // make sure the compiler doesn't toss it away
010A1000 mov ecx,dword ptr [__imp_std::cout (10A2048h)]
010A1006 push 2
010A1008 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (10A2044h)]
return 0;
010A100E xor eax,eax
Wie Sie sehen können, hat es lediglich das Auftreten von ersetzt x
mit einem statischen Wert von 2 und schob es für den Aufruf auf den Stapel cout
.Der Ausdruck wurde zur Laufzeit nicht ausgewertet.