Verständniss frage zu Zeigern
-
volkard schrieb:
[...]Die ofizielle Sichtweise, daß eine Referenz nur ein andere Name ist, halte ich für die flache Sicht[...]
Es ist aber die richtige Sicht. Ich könnte jetzt mehrere Beispiele, hauptsächlich in Verbindung mit Inlining und Templates ausführen, wo Referenzen
keineAdressmechanik im Hintergrund benutzen. Bei CPUs mit GPRs gibt es ebenfalls Fälle wo keine Zeiger werkeln.
Und ja, das wird in der Realität eingesetzt.
Es gibt natürlich auch viele Fälle, wo Referenzen über Zeigermechanik umgesetzt werden (geht dort auch nicht anders), aber daraus zu schließen, dass es deshalb immer so sein muss, ist ein Trugschluss .
-
volkard schrieb:
Tachyon schrieb:
mgaeckler schrieb:
Mehr als 30 Jahre Erfahrung. Selber schon bei einem Compilerbauer gearbeitet.
Sorry, dass ich das so sagen muss, aber dann bist Du offensichtlich nicht sehr tief in die Materie eingestiegen.
Aber er hat recht.
Die ofizielle Sichtweise, daß eine Referenz nur ein andere Name ist, halte ich für die flache Sicht.Die Sichtweise, daß eine Referenz ein verkappter Zeiger ist, halte ich für viel tragfähiger. Da muß man zwar die beiden EInträge der Symboltabelle auf das selbe Ding bei int i=5; int& j=i; als offensichtliche Optimierung erklären, aber hat sofort keine Schmerzen mehr bei sizeof, alignof und Strukturen mit Referenzen oder Referenzen als Übergabeparameter.
Allerdings geht es in dem Forum im die Sprache C++ und deren Anwendung in Programmen und gerade nicht primär um Details von Compilerimplementationen. Eine Interpretation, die für das eine nützlich ist, muss nicht unbedingt beim anderen sinnvoll sein (und umgekehrt).
Referenzen als verkappte Zeiger zu betrachten, hilft gerade nicht zu entscheiden, wann welches Sprachmittel zweckmäßig einzusetzen ist.
-
camper schrieb:
[...]
Das ist natürlich noch eine viel bessere Begründung als das, was hinter den Kulissen abläuft.

-
camper schrieb:
Referenzen als verkappte Zeiger zu betrachten, hilft gerade nicht zu entscheiden, wann welches Sprachmittel zweckmäßig einzusetzen ist.
Die andere Sichtweise hilft exakt genauso wenig.

Was hilft, ist die Überlegung, bei welchem Einsatz man Zeit fürs Debuggen spart.
-
Tachyon schrieb:
Es ist aber die richtige Sicht. Ich könnte jetzt mehrere Beispiele, hauptsächlich in Verbindung mit Inlining und Templates ausführen, wo Referenzen
keineAdressmechanik im Hintergrund benutzen. Bei CPUs mit GPRs gibt es ebenfalls Fälle wo keine Zeiger werkeln.
Und ja, das wird in der Realität eingesetzt.
Es gibt natürlich auch viele Fälle, wo Referenzen über Zeigermechanik umgesetzt werden (geht dort auch nicht anders), aber daraus zu schließen, dass es deshalb immer so sein muss, ist ein Trugschluss.Aber die zeigerlosen Fälle sind doch genauso als einfache Optimierung erklärbar.
-
Tachyon schrieb:
Es ist aber die richtige Sicht. Ich könnte jetzt mehrere Beispiele, hauptsächlich in Verbindung mit Inlining und Templates ausführen, wo Referenzen
keineAdressmechanik im Hintergrund benutzen. Bei CPUs mit GPRs gibt es ebenfalls Fälle wo keine Zeiger werkeln.
Und ja, das wird in der Realität eingesetzt.
Es gibt natürlich auch viele Fälle, wo Referenzen über Zeigermechanik umgesetzt werden (geht dort auch nicht anders), aber daraus zu schließen, dass es deshalb immer so sein muss, ist ein Trugschluss .Die Ausdrucksweise "wo keine Zeiger werkeln" zeigt, dass du die beiden Konzepte auf unterschiedlichen Ebenen betrachtest, Referenzen semantisch auf hoher Ebene, Zeiger auf niedriger. Was genau vergleichst du da eigentlich?
Es steht nirgendwo geschrieben, dass Zeiger mit "Adressmechanik" arbeiten müssen. Es bleibt einem in vielen Fällen nichts anderes übrig, aber wenn man sich auf die Verwendung der Mittel beschränkt, die auch mit Referenzen möglich sind, sollte der Compiler den gleichen Code erzeugen.
-
Bashar schrieb:
Die Ausdrucksweise "wo keine Zeiger werkeln" zeigt, dass du die beiden Konzepte auf unterschiedlichen Ebenen betrachtest, Referenzen semantisch auf hoher Ebene, Zeiger auf niedriger.
Was genau verleitet Dich zu der Annahme, dass das meine Sichtweise ist?
-
Bashar schrieb:
Die Ausdrucksweise "wo keine Zeiger werkeln" zeigt, dass du die beiden Konzepte auf unterschiedlichen Ebenen betrachtest, Referenzen semantisch auf hoher Ebene, Zeiger auf niedriger. Was genau vergleichst du da eigentlich?
Es steht nirgendwo geschrieben, dass Zeiger mit "Adressmechanik" arbeiten müssen. Es bleibt einem in vielen Fällen nichts anderes übrig, aber wenn man sich auf die Verwendung der Mittel beschränkt, die auch mit Referenzen möglich sind, sollte der Compiler den gleichen Code erzeugen.Amen.
Optimierungen, die der Compiler bei Referenzen anwenden kann, gelten i.d.R. auch für Zeiger.Es ist für einen Anfänger wichtig, zu verstehen, dass Referenzen nicht viel mehr als Zeiger mit anderer Syntax sind. Wenn das begriffen worden ist, dann kann man souverän urteilen, ob Referenzen oder Zeiger in einem bestimmten Fall angebrachter sind.
Es gibt erschreckend viele fortgeschrittene C++-Programmierer, die zwar mit Referenzen umgehen können, aber diese für sie intern immer noch eine recht mysteriöse Angelegenheit sind. Das trübt teilweise das Urteilsvermögen, denn recht weit verbreitet ist die Ansicht, dass Referenzen langsamer als Zeiger seien, da sie komplexer sind (manchmal hört man es auch umgekehrt). Anfänger glauben manchmal noch, dass eine Referenz ein lokales Objekt am Leben erhalten kann. Aber Referenzen sind eben weder komplexer noch intelligenter als Zeiger.Da es immer wieder Leute gibt, die behaupten, Zeiger seien etwas völlig anderes als Referenzen, nur weil im Standard nicht explizit steht, dass Referenzen durch Zeiger implementiert werden müssen, hilft das auch nicht gerade dem allgemeinen Verständnis.
Es ist nun mal so, dass sich eine Referenz in fast allen Fällen wie ein selbst-dereferenzierender Zeiger verhält - und das Verhalten dürfte im Standard sehr wohl vorgeschrieben sein - nur dass eine andere Wortwahl verwendet wird.
-
Tachyon schrieb:
mgaeckler schrieb:
Mehr als 30 Jahre Erfahrung. Selber schon bei einem Compilerbauer gearbeitet.
Sorry, dass ich das so sagen muss, aber dann bist Du offensichtlich nicht sehr tief in die Materie eingestiegen.
OK. Dann kläre mich mal auf. Zeige mir eine Implementierung mit Referenzen, die man nicht genauso gut auch mit Zeigern machen kann.
Ich behaupte nach wie vor: Referenzen sind nichts anderes als kastrierte Zeiger. Zeig mir ein Beispiel, das das Gegenteil beweist, und Du hast mich überzeugt.mfg Martin
P.S.: Und wenn Du mir nicht glaubst, vieleicht glaubst Du Stroustrup:
"Reference conversions behave much like pointer conversion" (M. A. Ellis, B. Stroustrup, The Annotated C++ Reference Manual, Addison Wesley, 1990, S. 38)
-
Bjarne Stroupstrup Die C++ Programmiersprache S.106 schrieb:
Die naheliegend Implementierung einer Referenz ist die als (konstanter) Zeiger, der bei jeder Benutzung derefenziert wird. Es richtig keine großen Schaden an, sich Referenzen so vorzustellen, solange man daran denkt, daß eine Referenz kein Objekt ist, das manipuliert werden kann wie ein Zeiger:
<Bild>
In einigen Fällen kann ein Compiler eine Referenz wegoptimieren, so daß zur Laufzeit kein Objekt gibt, dass die Referenz repräsentiertWer mal ein großeres Compilerbau-Buch in der Hand hatte, wird auch gelesen, dass es in Programmiersprachen gewollte redundanten Ausdrücke/Befehle gibst, die der Compiler in den meisten regeln auch wegoptimieren könnte, daher würde ich sorgar sagen, dass der zweite Fall sorgar ein Regelfall ist.
-
Athar schrieb:
Es ist für einen Anfänger wichtig, zu verstehen, dass Referenzen nicht viel mehr als Zeiger mit anderer Syntax sind. Wenn das begriffen worden ist, dann kann man souverän urteilen, ob Referenzen oder Zeiger in einem bestimmten Fall angebrachter sind.
Nein, für einen Anfänger ist es wichtig zu wissen, dass eine Referenz ein Aliasname für ein Objekt ist, während ein Zeiger eine Variable ist, die eine Adresse speichern kann. Wie eine Referenz intern umgesetzt ist, braucht den Anfänger dabei nicht zu interessieren.
Für den Compilerbauer ist es hingegen hilfreich zu wissen, dass eine Referenz einen Kontrakt darstellt, der an viel engere Rahmenbedingungen geknüpft ist, als ein Zeiger. Deshalb ermöglichen Referenzen in vielen Fällen Optimierungen, die so mit Zeigern nicht möglich wären.
Die Betrachtungsweise "Referenz == verkappter Zeiger" führt übrigens auch zu so tollen Aussagen wie: "Ich benutze lieber Zeiger, weil man da gleich sieht, dass das eine Referenz übergeben wird, und es eh keine Unterschiede gibt." Was für ein Schwachfug.
-
Tachyon schrieb:
Für den Compilerbauer ist es hingegen hilfreich zu wissen, dass eine Referenz einen Kontrakt darstellt, der an viel engere Rahmenbedingungen geknüpft ist, als ein Zeiger. Deshalb ermöglichen Referenzen in vielen Fällen Optimierungen, die so mit Zeigern nicht möglich wären.
Sorry, das ist nicht korrekt. Diese Optimierungen kann der Compiler nur dann machen, wenn er in dem jeweiligen Kontext sowohl die Referenz als auch das referenzierte Objekt kennt. Wenn er nur die Referenz kennt, sind die Optimierungen nicht mehr möglich. Bei Zeigern ist die Optimierung genauso möglich, wenn der Compiler sowohl den Zeiger als auch das Objekt kennt, auf das der Zeiger verweist.
Zeige mir ein Beispiel, wo es nicht so ist.
mfg Martin
-
Tachyon schrieb:
Die Betrachtungsweise "Referenz == verkappter Zeiger" führt übrigens auch zu so tollen Aussagen wie: "Ich benutze lieber Zeiger, weil man da gleich sieht, dass das eine Referenz übergeben wird,
Das ist kein Schwachfug.
Tachyon schrieb:
und es eh keine Unterschiede gibt." Was für ein Schwachfug.
Korrekt, das ist Unfug.
Wo sind denn nun die Unterschiede?
1. Referenzen müssen initialisiert werden und dabei auf ein gültiges Objekt verweisen. Zeiger dürfen eine beliebige Adresse enthalten und dürfen sogar uninitialisiert benutzt werden.
2. Referenzen kennen keine Pointerarithmetik. Referenzen können nur verändert werden, indem man sie auf ein anderes Objekt verweisen lässt.
3. Referenzen werden automatisch dereferenziert. Man kann sie also nicht dazu benutzen, um die Adresse des referenzierten Objektes zu erhalten.Was ist nun der Vorteil?
1. Da Referenzen initialisiert werden müssen, kann es nicht so leicht passieren, das ungültige Referenzen erzeugt werden. Unmöglich ist es aber nicht, wie ich schon gezeigt habe.
2. Da Referenzen automatisch dereferenziert werden, ist der Code leichter zu verstehen:void func1( const INT_ARRAY &ref ) { int value = ref[10]; }vs.
void func2( const INT_ARRAY *pointer ) { int value = (*pointer)[10]; ]Wenn ich eine Funktion habe, die ein größeres Objekt erhält, welches nicht verändert werden soll, benutze ich const Referenzen. Wenn das Objekt verändert werden soll, benutze ich Zeiger. Warum?
Nehmen wir an, ich habe ein Problem, daß ein Objekt einen falschen Wert erhält und ich weiß nicht wo. Im Debugger kann ich nun alle Funktionen überspringen, die nur eine Referenz erhalten, weil bei mir die Regel gilt keine Referenzen auf veränderbaren Objekten. Das spart viel Zeit bei der Fehleranalyse.
mfg Martin
-
Du hast einen Unterschied vergessen:
Zeiger sind der häufigste Grund für Programmabstürze! Nämlich dann, wenn sie auf nix zeigen und dereferenziert werden. Oder heißt es dann depointerisiert (ist ja keine Referenz)? Interessanterweise benutzt man auf bei Zeigern den Ausdruck "dereferenzieren", obwohl sie keine Referenzen sind. Faszinierend.
Mich würd noch interessieren, ob der Thread-Ersteller dieser Diskussion weiter folgt...
-
mgaeckler schrieb:
Referenzen können nur verändert werden, indem man sie auf ein anderes Objekt verweisen lässt.
Wie geht denn sowas?
-
mgaeckler schrieb:
[...]
2. Referenzen kennen keine Pointerarithmetik. Referenzen können nur verändert werden, indem man sie auf ein anderes Objekt verweisen lässt.
3. Referenzen werden automatisch dereferenziert. Man kann sie also nicht dazu benutzen, um die Adresse des referenzierten Objektes zu erhalten.
[...]100. Du hast Referenzen nicht verstanden.
Zu 2: Man kann Referenzen nicht auf ein anderes Objekt verweisen lassen nachdem sie initialisiert wurden.
Zu 3: Klar kann man das. Man kommt nur nicht an die Adresse des "Referenzobjekts" heran, weil die Referenz kein Objekt ist.
-
mgaeckler schrieb:
Wo sind denn nun die Unterschiede?
1. Referenzen müssen initialisiert werden und dabei auf ein gültiges Objekt verweisen. Zeiger dürfen eine beliebige Adresse enthalten und dürfen sogar uninitialisiert benutzt werden.
2. Referenzen kennen keine Pointerarithmetik. Referenzen können nur verändert werden, indem man sie auf ein anderes Objekt verweisen lässt.
3. Referenzen werden automatisch dereferenziert. Man kann sie also nicht dazu benutzen, um die Adresse des referenzierten Objektes zu erhalten.2 und 3 sind klar falsch. Erstens können Referenzen nach der Initialisierung nicht mehr verändert werden, zweitens ist es sehr wohl möglich, die Adresse des referenzierten Objektes zu erhalten:
T t; T& r = t; T* p = &r; // == &t2. Da Referenzen automatisch dereferenziert werden, ist der Code leichter zu verstehen:
void func1( const INT_ARRAY &ref ) { int value = ref[10]; }vs.
void func2( const INT_ARRAY *pointer ) { int value = (*pointer)[10]; ]Das Beispiel hätte man besser ohne Pointer oder Referenz geschrieben, da Arrays ohnehin als Pointer übergeben werden. Im Allgemeinen ist deine Aussage nämlich nicht zu halten.
-
mgaeckler schrieb:
Sorry, das ist nicht korrekt. Diese Optimierungen kann der Compiler nur dann machen, wenn er in dem jeweiligen Kontext sowohl die [...]
Tja, da Du nicht wusstest, dass gebundene Refenezen wärend ihrer Lebenszeit konstant bleiben...

Pointer sind variabel. Es ist viel schwieriger zu sie optimieren (vor allem, wenn der Code der sie benutzt nicht nur ein exemplarischer Einzeiler ist) da man damit rechnen muss, dass sie neu belegt werden.
Referenzen sind, wie gesagt für ihre Lebenszeit immer an das gleiche Objekt gebunden. Das ermöglicht Optimierungen auch ohne den Kontext zu kennen...
-
mgaeckler schrieb:
Nehmen wir an, ich habe ein Problem, daß ein Objekt einen falschen Wert erhält und ich weiß nicht wo. Im Debugger kann ich nun alle Funktionen überspringen, die nur eine Referenz erhalten, weil bei mir die Regel gilt keine Referenzen auf veränderbaren Objekten. Das spart viel Zeit bei der Fehleranalyse.
Dafür gibt es Watchpoints, das ist also nicht unbedingt ein Argument.
-
Bashar schrieb:
Das Beispiel hätte man besser ohne Pointer oder Referenz geschrieben, da Arrays ohnehin als Pointer übergeben werden. Im Allgemeinen ist deine Aussage nämlich nicht zu halten.
typedef int ARRAY[10]; void f1(ARRAY a) { std::cout << sizeof(a) << '\n'; } void f2(ARRAY & a) { std::cout << sizeof(a) << '\n'; } int main() { ARRAY a = {0}; f1(a); f2(a); }Merke: Array != Pointer => Array & != Pointer &...