Ich weiß, dass es die Lesbarkeit verbessert und das Programm weniger fehleranfällig macht, aber um wie viel verbessert es die Leistung?
Und was ist der Hauptunterschied zwischen einer Referenz und einem const
Zeiger? Ich würde annehmen, dass sie anders im Speicher gespeichert sind, aber wie?
const
, aber hauptsächlich für Menschen und den Code, den wir schreiben. Der Compiler kann nichts vertrauen und kann daher nicht in gleicher Weise davon profitieren.Antworten:
[Bearbeiten: OK, diese Frage ist subtiler als ich zuerst dachte.]
Das Deklarieren eines Zeigers auf const oder einer Referenz von const hilft keinem Compiler, etwas zu optimieren. (Siehe auch das Update am Ende dieser Antwort.)
Die
const
Erklärung gibt nur an, wie ein Bezeichner im Rahmen seiner Erklärung verwendet wird. Es heißt nicht, dass sich das zugrunde liegende Objekt nicht ändern kann.Beispiel:
int foo(const int *p) { int x = *p; bar(x); x = *p; return x; }
Der Compiler kann nicht davon ausgehen, dass
*p
dies durch den Aufruf von nicht geändert wirdbar()
, dap
dies (z. B.) ein Zeiger auf ein globales int sein und esbar()
möglicherweise ändern kann.Wenn der Compiler genug über den Aufrufer von weiß
foo()
und der Inhalt, vonbar()
dem er beweisen kann, dass erbar()
sich nicht ändert*p
, kann er diesen Beweis auch ohne die const-Deklaration ausführen .Dies gilt jedoch im Allgemeinen. Da sich dies
const
nur im Bereich der Deklaration auswirkt, kann der Compiler bereits sehen, wie Sie den Zeiger oder die Referenz in diesem Bereich behandeln. Es ist bereits bekannt, dass Sie das zugrunde liegende Objekt nicht ändern.Kurz gesagt, alle
const
in diesem Zusammenhang tun, ist zu verhindern, dass Sie Fehler machen. Es sagt dem Compiler nichts, was er noch nicht weiß, und ist daher für die Optimierung irrelevant.Was ist mit Funktionen, die aufrufen
foo()
? Mögen:int x = 37; foo(&x); printf("%d\n", x);
Kann der Compiler beweisen, dass dies 37 druckt, da
foo()
einconst int *
?Nein. Auch wenn
foo()
ein Zeiger auf const benötigt wird, kann dies die Konstanz wegwerfen und das int ändern. (Dies ist kein undefiniertes Verhalten.) Auch hier kann der Compiler im Allgemeinen keine Annahmen treffen. und wenn es genug weiß, umfoo()
eine solche Optimierung vorzunehmen, wird es das auch ohne die wissenconst
.Die einzige Zeit
const
, in der Optimierungen möglich sind, sind folgende Fälle:const int x = 37; foo(&x); printf("%d\n", x);
Hier bedeutet das Modifizieren
x
durch irgendeinen Mechanismus (z. B. indem man einen Zeiger darauf nimmt und den wegwirftconst
), dass man undefiniertes Verhalten aufruft. Der Compiler kann also davon ausgehen, dass Sie dies nicht tun, und er kann die Konstante 37 in printf () übertragen. Diese Art der Optimierung ist für jedes von Ihnen deklarierte Objekt zulässigconst
. (In der Praxis ist eine lokale Variable, auf die Sie niemals verweisen, nicht von Vorteil, da der Compiler bereits sehen kann, ob Sie sie in seinem Umfang ändern.)Um Ihre "Randnotiz" -Frage zu beantworten, (a) ist ein const-Zeiger ein Zeiger; und (b) ein konstanter Zeiger kann gleich NULL sein. Sie haben Recht, dass die interne Darstellung (dh eine Adresse) höchstwahrscheinlich dieselbe ist.
[aktualisieren]
Wie Christoph in den Kommentaren betont, ist meine Antwort unvollständig, weil sie nicht erwähnt wird
restrict
.In Abschnitt 6.7.3.1 (4) der C99-Norm heißt es:
(Hier ist B ein Basisblock, über den sich P, ein Beschränkungszeiger auf T, im Geltungsbereich befindet.)
Wenn also eine C-Funktion
foo()
wie folgt deklariert ist:foo(const int * restrict p)
... dann der Compiler kann davon ausgehen , dass keine Änderungen
*p
während der Laufzeit auftretenp
- dh während der Ausführungfoo()
- weil sonst das Verhalten nicht definiert wäre.Also im Prinzip kombinieren
restrict
mit einem Zeiger auf const beide Optimierungen ermöglichen, die oben verworfen wurden. Implementieren Compiler tatsächlich eine solche Optimierung, frage ich mich? (GCC 4.5.2 zumindest nicht.)Beachten Sie, dass
restrict
nur in C vorhanden ist, nicht in C ++ (nicht einmal in C ++ 0x), außer als compilerspezifische Erweiterung.quelle
const
Variablen überconst_cast
ruft UB auf, nicht nur globaleconst
...const
, ist aber zumindest unvollständig - Nemo hat vergessenrestrict
: zu deklarieren,foo()
wieint foo(const int *restrict p)
dies dem Compiler tatsächlich sagen würde, dass der Wert, auf den vonp
zeigt, durch einen Aufruf nicht geändert wird vonfoo()
restrict
tatsächlich bedeutet, ist, dass (1) wenn ein Objekt, auf das einrestrict
qualifizierter Zeiger zeigt, geändert wird, der gesamte Zugriff (einschließlich des Objekts , das die Änderung ausgelöst hat) darauf basieren muss Zeiger und (2) - der Teil, der hier relevant ist - wenn eine Änderungconst
auftritt, darf der Zeiger nicht qualifiziert werden ; Fazit: Es ist illegal, Objekte zu ändern, auf die durchrestrict
qualifizierte Zeigerconst
während der Lebensdauer dieses Zeigers verwiesen wirdconst
In C ++ gibt es zwei Probleme (was die Optimierung betrifft):const_cast
mutable
const_cast
Dies bedeutet, dass die Funktion die Konstanz möglicherweise wegwirft und das Objekt ändert, obwohl Sie ein Objekt als Konstantenreferenz oder Konstantenzeiger übergeben (zulässig, wenn das Objekt zunächst keine Konstante ist).mutable
Dies bedeutet, dassconst
einige Teile eines Objekts möglicherweise geändert werden (Caching-Verhalten). Objekte, auf die verwiesen wird (anstatt Eigentum zu sein), können inconst
Methoden geändert werden, selbst wenn sie logisch Teil des Objektstatus sind. Und schließlich können auch globale Variablen geändert werden ...const
ist hier, um dem Entwickler zu helfen, logische Fehler frühzeitig zu erkennen.quelle
const_cast
und das Objekt dann ändert, ist dies ein undefiniertes Verhalten. Compiler können davon ausgehen, dass dieser Fall niemals auftritt.const
Objekt zu ändern , aber es ist der Grund,const_cast
dasconst
Attribut einer Referenz oder eines Zeigers wegzuwerfen , um ein Nicht-const
Objekt zu ändern . Der Compiler kann also einige Leistungssteigerungen erzielenconst
, nur einen Bruchteil dessen, worauf er verzichten könnteconst_cast
.Ich kann mir zwei Fälle
const
vorstellen, in denen eine ordnungsgemäße Qualifizierung zusätzliche Optimierungen ermöglicht (in Fällen, in denen keine Analyse des gesamten Programms verfügbar ist):const int foo = 42; bar(&foo); printf("%i", foo);
Hier kann der Compiler drucken,
42
ohne den Hauptteil vonbar()
(der in der aktuellen Übersetzungseinheit möglicherweise nicht sichtbar ist) untersuchen zu müssen, da alle Änderungenfoo
unzulässig sind ( dies entspricht dem Beispiel von Nemo ).Dies ist jedoch auch ohne Kennzeichnung
foo
alsconst
durch Deklarationbar()
als möglichextern void bar(const int *restrict p);
In vielen Fällen möchte der Programmierer tatsächlich
restrict
qualifizierte Zeiger aufconst
und keine einfachen Zeiger aufconst
als Funktionsparameter, da nur die ersteren Garantien für die Veränderbarkeit der Objekte geben, auf die verwiesen wird.Zum zweiten Teil Ihrer Frage: Für alle praktischen Zwecke kann eine C ++ - Referenz als konstanter Zeiger (kein Zeiger auf einen konstanten Wert!) Mit automatischer Indirektion betrachtet werden - sie ist nicht "sicherer" oder "schneller". als ein Zeiger, nur bequemer.
quelle
Konst-Korrektheit hilft im Allgemeinen nicht der Leistung; Die meisten Compiler machen sich nicht einmal die Mühe, die Konstanz über das Frontend hinaus zu verfolgen. Das Markieren von Variablen als const kann je nach Situation hilfreich sein.
Referenzen und Zeiger werden genauso im Speicher gespeichert.
quelle
Dies hängt wirklich vom Compiler / der Plattform ab (es kann bei einigen noch nicht geschriebenen Compilern oder auf einer Plattform, die Sie nie verwenden, zur Optimierung beitragen). Die C- und C ++ - Standards sagen nichts über die Leistung aus, außer dass für einige Funktionen Komplexitätsanforderungen gestellt werden.
Zeiger und Verweise auf const helfen normalerweise nicht bei der Optimierung, da die const-Qualifikation in einigen Situationen legal weggeworfen werden kann und es möglich ist, dass das Objekt durch eine andere Nicht-const-Referenz geändert werden kann. Andererseits kann es hilfreich sein, ein Objekt als const zu deklarieren, da es garantiert, dass das Objekt nicht geändert werden kann (selbst wenn es an Funktionen übergeben wird, deren Definition der Compiler nicht kennt). Auf diese Weise kann der Compiler das const-Objekt im Nur-Lese-Speicher speichern oder seinen Wert an einem zentralen Ort zwischenspeichern, wodurch der Bedarf an Kopien und Überprüfungen auf Änderungen verringert wird.
Zeiger und Referenzen werden normalerweise genauso implementiert, aber auch dies ist völlig plattformabhängig. Wenn Sie wirklich interessiert sind, sollten Sie sich den generierten Maschinencode für Ihre Plattform und Ihren Compiler in Ihrem Programm ansehen (wenn Sie tatsächlich einen Compiler verwenden, der Maschinencode generiert).
quelle
Eine Sache ist, wenn Sie eine globale Variable const deklarieren, ist es möglicherweise möglich, sie in den schreibgeschützten Teil einer Bibliothek oder ausführbaren Datei zu legen und sie somit mit einer schreibgeschützten mmap für mehrere Prozesse freizugeben. Dies kann unter Linux ein großer Speichergewinn sein, zumindest wenn viele Daten in globalen Variablen deklariert sind.
In einer anderen Situation kann der Compiler, wenn Sie eine konstante globale Ganzzahl oder float oder enum deklarieren, die Konstante möglicherweise nur inline setzen, anstatt eine Variablenreferenz zu verwenden. Ich glaube, das ist ein bisschen schneller, obwohl ich kein Compiler-Experte bin.
Referenzen sind nur Hinweise darauf, was die Implementierung betrifft.
quelle
const
oder nicht.extern
Variablen.extern
Stopps viel bedeuten.Dies kann die Leistung ein wenig verbessern, jedoch nur, wenn Sie über die Deklaration direkt auf das Objekt zugreifen. Referenzparameter und dergleichen können nicht optimiert werden, da es möglicherweise andere Pfade zu einem Objekt gibt, das ursprünglich nicht als const deklariert wurde, und der Compiler im Allgemeinen nicht feststellen kann, ob das Objekt, auf das Sie verweisen, tatsächlich als const deklariert wurde oder nicht, es sei denn, dies ist die von Ihnen verwendete Deklaration.
Wenn Sie eine const-Deklaration verwenden, weiß der Compiler, dass extern kompilierte Funktionskörper usw. diese nicht ändern können, sodass Sie dort einen Vorteil erhalten. Und natürlich werden Dinge wie const int's zur Kompilierungszeit weitergegeben, das ist also ein großer Gewinn (im Vergleich zu nur einem int).
Referenzen und Zeiger werden genau gleich gespeichert, sie verhalten sich nur syntaktisch unterschiedlich. Referenzen sind im Grunde genommen Umbenennungen und daher relativ sicher, während Zeiger auf viele verschiedene Dinge verweisen können und daher leistungsfähiger und fehleranfälliger sind.
Ich denke, der const-Zeiger wäre architektonisch identisch mit der Referenz, also wären der Maschinencode und die Effizienz gleich. Der eigentliche Unterschied liegt in der Syntax. Referenzen sind eine sauberere und leichter zu lesende Syntax. Da Sie die zusätzliche Maschinerie eines Zeigers nicht benötigen, wird eine Referenz stilistisch bevorzugt.
quelle
const
Ordnung ist, ohne dass Sie es sagen. (Apropos aktuelle MSVC ++ und GCCs, beide haben Link Time Code Generierung)const
Qualifikation von Variablen, die er auch nicht sehen kann, niemals vertrauen . Dieser Speicher kann durch völlig andere Sprachen, in denen erconst
nicht existiert, durcheinander gebracht werden , oder er kann sogar in einem Raw-Assembler geschrieben werden. In solchen Fällen, in denen eineconst
Variable dieser Bibliothek zugänglich gemacht wird, muss der Compiler die const-Qualifikation ignorieren. Daher sehe ich nicht, wie relevant das für diese Frage ist.