Der unterschied zwischen pointer und referenze im c

  • Forum
    • µC & Elektronik
    • Analogtechnik
    • FPGA, VHDL & Co.
    • DSP
    • Compiler & IDEs
    • Projekte & Code
    • Markt
    • Platinen
    • Mechanik & Werkzeug
    • HF, Funk & Felder
    • Fahrzeugelektronik
    • Haus & Smart Home
    • PC-Programmierung
    • PC Hard- & Software
    • Ausbildung & Beruf
    • Offtopic
    • Webseite
  • Artikelübersicht
  • Letzte Änderungen

     
    • Forenliste
    • Threadliste
    • Neuer Beitrag
    • Suchen
    • Benutzerliste
    • Bildergalerie
    • Hilfe
    • Anmelden
    • Login


Forum: Compiler & IDEs Unterschied zwischen Zeiger und Referenz



Hallo Leute, mir ist der Unterschied zwischen einem Zeiger und einer Referenz nicht so ganz klar. Ein Beispiel: (siehe hier: http://www.highprogrammer.com/alan/rants/mutable.html )

1 class Person {
2 public:
3 virtual bool has_pulse() const { return true; }
4 void set_name() { /* ... */ }
5 };
6
7 class Robot : public Person {
8 public:
9 virtual bool has_pulse() const { return false; }
10 void set_name() { /* ... */ }
11 };
12
13 /*
14 Because Person is const, take_pulse cannot call set_name().
15 Because Person is a reference, we can pass in a Robot robot
16 and get the correct answer (false).
17 */
18 bool take_pulse( const Person & X ) {
19 return X.has_pulse();
20 }

Warum wird bei der Funktion

1 bool take_pulse( const Person & X )

als Argumentund nicht oderuebergeben? Wo ist da jetzt genau der Unterschied, und wann nimmt man welche Variante? Gruesse


von Torsten Robitzki (Gast)

19.12.2014 12:55



Technisch sind beide gleich. Wenn ich anzeigen möchte, dass eine Referenz optional ist. Also entweder auf ein gültiges Objekt zeigt, oder aber auf nichts zeigt, dann verwendet man einen Zeiger ansonsten eine Referenz. Eine Referenz referenziert immer ein gültiges Objekt (wenn nicht, hat man undefiniertes Verhalten; also ein Fehler in der SW). Das ist auch der Grund, warum ich eine Referenz immer initialisieren muss, einen Zeiger aber nicht. Eine Referenz kann ich später auch nicht mehr ändern. Sprich die Identität des Objekt, auf das ich referenziere bleibt immer die selbe. Es gibt sicher noch ein paar Spezialfälle, wo es sinnvoller ist, einen Zeiger zu verwenden, der dann eben nicht 0 sein darf. Z.B. wenn man einer Funktion einen Puffer übergeben möchte. Dann verwendet man in der Regel einen Zeiger auf das erste Element eines Arrays und das sollte man auch weiter so beibehalten, weil es einfach idiomatisch ist. mfg Torsten


von Kaj G. (Firma: RUB) (bloody)

23.12.2014 10:53



Ok, vielen dank fuer die Antwort. :)


von Sebastian V. (sebi_s)

23.12.2014 23:06



Einige Sachen funktionieren aber nur mit References. Zum Beispiel kann man mit Pointern nur Arrays übergeben indem man auf das erste Element zeigt. Dabei geht die Größe des Arrays verloren (array to pointer decay). Mit References kann man tatsächlich eine Reference auf ein Array haben (wahlweise mit der Größe als Template Parameter). Ein Unterschied zwischen Pointer und Reference ist natürlich auch, dass man bei References kein & (Address Operator) vor die Parameter zu schreiben braucht. Das ist spätestens bei Operator Overloading praktisch. Ich glaube niemand wollte Code wie

1 BigInt a, b;
2 BigInt* c = &a + &b;

schreiben. Ansonsten kann ich dem Beitrag von Torsten nur zustimmen.


von mem (Gast)

24.12.2014 07:58



Wie sieht dass denn im Speicher aus? Wieviel Speicher belegt ein pointer und wieviel eine Referenz?


von Ralf G. (ralg)

24.12.2014 09:48



mem schrieb: > Wieviel Speicher belegt ein pointer und wieviel eine Referenz? Torsten Robitzki schrieb: > Technisch sind beide gleich.



mem schrieb: > Wie sieht dass denn im Speicher aus? > Wieviel Speicher belegt ein pointer und wieviel eine Referenz? Offiziell (also laut ISO-Norm) ist eine Referenz selbst kein Objekt, sondern nur ein anderer Name für ein bereits bestehendes Objekt und belegt daher keinen eigenen Speicher. Deshalb kann man mit sizeof nicht die Größe einer Referenz ermitteln und auch nicht eine mit dem operator new dynamisch erzeugen. Auch die Adresse einer Referenz läßt sich nicht ermitteln. Um eine Referenz zu implementieren, wird aber je nach Anwendungsfall ein gewisser zusätzlicher Platz im Speicher benötigt. Unter der Haube werden Referenzen dann typischerweise genau wie Zeiger implementiert. Das ist dann aber ein Implementierungsdetail des Compilers.


24.12.2014 10:08: Bearbeitet durch User

von mem (Gast)

24.12.2014 12:39



Aha, gut das ich gefragt habe. ;-)



Rolf Magnus schrieb: > Offiziell (also laut ISO-Norm) ist eine Referenz selbst kein Objekt, > sondern nur ein anderer Name für ein bereits bestehendes Objekt und > belegt daher keinen eigenen Speicher. Deshalb kann man mit sizeof nicht > die Größe einer Referenz ermitteln und auch nicht eine mit dem operator > new dynamisch erzeugen. Zur Klarstellung: Man kann sehr wohl den Adressoperator auf eine Referenz anwenden und bekommt die Adresse des referenzierten Objekts, analog mit sizeof. Was du wohl meinst, ist daß man an eventuelle Hilfsdaten (heimlicher Zeiger o.ä.) nicht herankommt.


von mem (Gast)

24.12.2014 14:07



Was erhalte ich vom Adressoperator bei einem pointer und was bei einer Referenz?



mem schrieb: > Was erhalte ich vom Adressoperator bei einem pointer und was bei einer > Referenz?

1 int * pI;
2
3 sizeof( pI ) -> die Größe der Pointervariablen
4 sizeof( *pI ) -> die Größe dessen, worauf der Pointer zeigt. In
5 diesem Fall die Größe eines int
6
7
8 int i;
9 int& j = i;
10
11 sizeof(j) -> ist dasselbe wie sizeof(i). Die Größe eines int

Nachdem eine Referenz eingerichtet wurde, gibt es keinen UNterschied mehr, ob du die Referenz benutzt oder direkt das originale Objekt. Eine Referenz ist nur ein anderer Name für ein anderes Objekt. Du kannst deine Frau ja auch 'Helga' oder 'Schatzi' nennen. Aber egal welchen Namen du benutzt, es ist immer dieselbe Frau. So auch hier: es ist Speicher reserviert worden, der so groß ist, dass ein int da hineinpasst. Aber egal ob du den Variablennamen i oder den Variablennamen j benutzt, es ist immer genau derselbe Speicherbereich damit gemeint. VOn daher bin ich mit der Aussage, dass Referenzen und Pointer technisch identisch sind eher unglücklich. Denn das sind sie nicht. Die Semantik von Referenzen wird oft mit einem Pointer im Hintergrund realisiert, aber das deckt nicht alle Fälle ab und ist für das Verständnis von Referenzen nicht hilfreich. Es ist ein Implementierungsdetail, denn irgendwie müssen Referenzen ja tatsächlich realisiert werden und das geht in manchen Fällen nicht anders. Aber es ist nur die halbe Wahrheit. Im obigen Beispiel wird der Compiler zb für die realisierung von j eben keinen Pointer benutzen, sondern sich in seinen Tabellen eintragen, dass eigentlich i gemeint ist, wann immer du j benutzt (bzw. das j denselben Speicherbereich kennzeichnet wie i). Eben ein anderer Name für dasselbe Objekt (= denselben Speicherbereich)


24.12.2014 14:21: Bearbeitet durch User

von mem (Gast)

24.12.2014 15:21



sizeof ist zwar nicht der Adressoperator, aber du hast die Lunte gerochen. ;-) Eine Referenz ist nur ein anderer Name. Und ein pointer ein eigener Datentyp. Für mich sind das zwei völlig verschiedene Dinge, auch wenn sie nach aussen gleich erscheinen. Ein array ist ja auch kein pointer. Aber das ist eine gaaannnzzz andere Sache. ;-) Frohes Fest!


von Rufus Τ. F. (rufus)

Der unterschied zwischen pointer und referenze im c

24.12.2014 15:23



Karl Heinz schrieb: > VOn daher bin ich mit der Aussage, dass Referenzen und Pointer technisch > identisch sind eher unglücklich. Inhaltlich aber ist das nicht ganz falsch, wenn so etwas an eine Funktion übergeben wird. Aber auch nicht komplett synonym ...

1 void blafusel(int& x)
2 {
3 x = 4;
4 }
5
6 void machwas(void)
7 {
8 int i = 0;
9
10 blafusel(i);
11
12 printf("%d\n", i);
13 }

Hier zeigt sich auch etwas, was ich als Nachteil, oder zumindes sehr gewöhnungsbedürftige Eigenschaft von Referenzen ansehe. In C (das keine Referenzen kennt) kann man bei alleiniger Betrachtung der Funktion "machwas" klar sehen, daß "i" als 0 ausgegeben werden wird, da der Funktionsaufruf von "blafusel" die Variable nicht verändern kann. In C genügt es dafür, den Aufruf einer Funktion zu sehen, ohne sich die Funktion selbst oder deren Prototyp anzusehen. In C++ (mit Referenzen) genügt das nicht; obiges Beispiel zeigt das. Somit ist C++-Code definitiv anders (und mit mehr Vorsicht) zu lesen als C-Code, weil etwas, was in C ein Objekt nicht verändern kann, in C++ das sehr wohl kann. (Ob es das letztlich tut, ist wieder eine ganz andere Angelegenheit) Das ist aber in Pascal-artigen Sprachen auch nicht anders, dort lässt sich bei alleiniger Betrachtung des Aufrufs einer Funktion (oder Prozedur) nicht erkennen, ob dort "call by value" oder "call by reference" durchgeführt wird. Um das zu erkennen, muss man die Funktions/Prozedurdefinition ansehen. Klar, wenn man eigenen Code schreibt, ist das kein relevantes Problem, aber wenn man fremden Code durchliest, dann ist so etwas nicht ganz bedeutungslos.


24.12.2014 15:23: Bearbeitet durch User


Rufus Τ. Firefly schrieb: > In C (das keine Referenzen kennt) kann man bei alleiniger > Betrachtung der Funktion "machwas" klar sehen, daß "i" als 0 > ausgegeben werden wird, da der Funktionsaufruf von "blafusel" die > Variable nicht verändern kann. Es sei denn, "blafusel" ist ein Makro oder i ist en Array. Das tolle an C ist, daß es bei allen Regeln Ausnahmen gibt. Generell ist es keine gute Idee, Funktionen aufzurufen, ohne zu wissen, was sie mit den Parametern machen, egal ob in C oder C++. Da gibt es noch viel mehr Stolperfallen, als eine den übergebnen Wert ändernde Funktion. Es ist halt nur anfangs ungewohnt, daß das in C++ mit Referenzen möglich ist.


von Rufus Τ. F. (rufus)

Der unterschied zwischen pointer und referenze im c

25.12.2014 00:42



Rolf Magnus schrieb: > Es sei denn, "blafusel" ist ein Makro oder i ist en Array. Welchen Typ i hat, sieht man in diesem Fall, da es hier in der Nähe des Aufrufs von "blafusel" definiert wird. "blafusel" sollte kein Makro sein, sofern der Programmierer sich an die Konventionen gehalten hat -- Makronamen werden in VERSALIEN geschrieben. Allerdings ist das bei fremden Quelltexten keine Annahme, von der man unbedingt ausgehen kann. Insofern hast Du natürlich recht.


von Hans (Gast)

25.12.2014 11:50



In manchen Coderichtlinien (u.a. Google C++ Style Guide) wird deshalb empfohlen, Parameter an Funktionen nur per Value, per Const-Referenz oder per (Non-Const-)Zeiger zu übergeben. Wenn man sich daran hält, ist beim Aufruf auf einen Blick ersichtlich, was als Ein- und Ausgabeparameter gedacht ist. Ich nutzte außerdem Zeiger statt Referenzen, wenn sich die Funktion die Adresse über den Funktionsaufruf hinweg merkt. Pass-by-Const-Reference ist dann einfach eine effizientere Form von Pass-by-Value, die sich aber semantisch und syntaktisch gleich verhält.


von Willi Wampe (Gast)

25.12.2014 14:46



> Pass-by-Const-Reference ist dann einfach eine effizientere Form von > Pass-by-Value Aber auch nur bei größeren Objekten ("größer" ist hier natürlich relativ und von der Umgebung abhängig). Ich habe schon "const long&" u.ä. als Parameter gesehen ... > die sich aber semantisch und syntaktisch gleich verhält. void hmmm(int x) { x = 42; } void hmmm(const int& x) { const_cast<int&>(x) = 42; }



Willi Wampe schrieb: >> die sich aber semantisch und syntaktisch gleich verhält. > > void hmmm(int x) > { > x = 42; > } > > void hmmm(const int& x) > { > const_cast<int&>(x) = 42; > } Und was willst du damit sagen, außer daß const_cast immer im Zusammenhang mit einem Programmierfehler auftaucht?


von Torsten Robitzki (Gast)

28.12.2014 17:05



Hans schrieb: > In manchen Coderichtlinien (u.a. Google C++ Style Guide) wird > deshalb > empfohlen, Parameter an Funktionen nur per Value, per Const-Referenz > oder per (Non-Const-)Zeiger zu übergeben. > > Wenn man sich daran hält, ist beim Aufruf auf einen Blick ersichtlich, > was als Ein- und Ausgabeparameter gedacht ist. In komplexeren Beispielen ist dies aber meist schon sehr schnell nicht mehr anwendbar:

1 struct result {...};
2
3 void add_bar(result*);
4 void add_foo(result*);
5
6 void fill_in_root_cause(result* r)
7 {
8 assert(r);
9
10 add_bar(r);
11 add_foo(r);
12 }
13
14 int main()
15 {
16 result r;
17 ...
18 fill_in_root_cause(&r);
19 }

hier ist jetzt nur noch an einer von drei Stellen sichtbar, dass der Parameter geändert wird. Und das Beispiel ist jetzt auch nicht wirklich besonders konstruiert. Wenn Du z.B. so etwas wie eine Senke für ein Protokoll hast, dann wird diese Senke sicher über mehrere Ebenen weiter gereicht. Zusätzlich handelt man sich die Möglichkeit ein, dass der übergebene Parameter 0 sein kann. Die einzige, wirklich funktionierende Vorgehensweise, ist meiner Meinung nach vernünftige Namen für Variablen und Funktionen zu verwenden. mfg Torsten


von N.R. (Gast)

03.01.2015 21:58



N'abend, browse gerade durch die Threads und kann mich nicht zurück halten... 1. Cool bei Referenzen ist die Funktionsübergabe von fixed sized arrays (compile time checked) also etwas wie "int (&array)[100]". Das geht nicht in C. ... 2. Schön bei Referenzen als Parameter ist der implizite Ausschlus von NULL Pointern (wenn man nicht gerade castet)... 3. Const References als Funktionsparameter sind ok (im Sinn von Punkt 2)... 4. Non-Const References als Funktionsparameter oder Rückgabewerte sind verwirrend, wenn man nur die Caller-Side sieht: Soll heißen, man sieht nicht ob das ein Value oder eine "Reference" ist und weiß somit nicht ob bei Modifikationen das Urpsungsobjekt oder eine Kopie verändert wird... (ich habe eine Zeitlang statt dessen Pointer benutzt, ist aber auch keine optimale Lösung)... 5. Eine Reference als Object Variable ist interessant... (don't do it unless you know what you do). Grüße btw: Ich mag Karl Heinz' Beiträge!


von Yalu X. (yalu) (Moderator)

04.01.2015 12:24



Sebastian V. O. schrieb: > Einige Sachen funktionieren aber nur mit References. Zum Beispiel kann > man mit Pointern nur Arrays übergeben indem man auf das erste Element > zeigt. Dabei geht die Größe des Arrays verloren (array to pointer > decay). Mit References kann man tatsächlich eine Reference auf ein Array > haben N.R. schrieb: > 1. Cool bei Referenzen ist die Funktionsübergabe von fixed sized arrays > (compile time checked) also etwas wie "int (&array)[100]". Das geht > nicht in C. ... Man kann doch in C auch einen Zeiger auf ein Array übergeben (nicht nur einen auf dessen erstes Element). Man muss diesen Zeiger halt an den Stellen, wo man das Array benutzen möchte, mit dem *-Operator dereferenzieren. Das ist aber immer so, wenn man C++Referenzen durch Zeiger nachbildet. Eine Referenz in C++ ist im Wesentlichen ein selbstdereferenzierender Zeiger mit ein paar durch diese automatische Dereferenzierung bedingten Einschränkungen (z.B. keine Zuweisung möglich, deswegen Initialisierung immer erforderlich). Man kann praktisch alles, was mit Referenzen möglich ist, mit gewöhnlichen Zeigern nachbilden, allerdings entsteht dabei etwas mehr Schreibarbeit, da man häufiger die *- und &-Operatoren bemühen muss.


von Sebastian V. (sebi_s)

04.01.2015 12:56



Yalu X. schrieb: > Man kann doch in C auch einen Zeiger auf ein Array übergeben (nicht nur > einen auf dessen erstes Element). Stimmt, da hast du recht. Mir ist gerade aber noch eine andere Eigenheit von References in C++ eingefallen. Wenn man const References als Parameter von Funktionen hat, dann verhält es sich ziemlich genau wie wenn ich direkt den Wert übergeben hätte. Insbesondere ist auch sowas erlaubt:

1 void foo(const MyClass& x) { ... }
2 ...
3 foo(MyClass());

Also einen temporären Wert übergeben. Der C++ Standard garantiert hier, dass der Parameter bis nach dem Funktionsaufruf erhalten bleiben. Dies gilt aber nur für const References! Bei einer normalen Reference wird das temporäre Objekt in der Regel vorm Funktionsaufruf wieder zerstört. Visual Studio hat hier aber wieder seine Eigenheiten und eine nicht standardisierte Erweiterung die auch nicht const References erlaubt.


04.01.2015 12:56: Bearbeitet durch User

von Nase (Gast)

04.01.2015 15:20



Rufus Τ. Firefly schrieb: > Makronamen werden in VERSALIEN > geschrieben. Die intention ist schön, wird aber schon in der Standard-C-Bibliothek nicht eingehalten... N.R. schrieb: > Cool bei Referenzen ist die Funktionsübergabe von fixed sized arrays Cool ist auch, dass man in c++ einen vector verwenden kann. Der hat eine Größeninformation und braucht kaum mehr Speicher. Sebastian V. O. schrieb: > Bei einer normalen Reference wird > das temporäre Objekt in der Regel vorm Funktionsaufruf wieder zerstört. An eine normale Reference darfst du mit einem gescheiten Compiler garkeine temporären Werte übergeben. Da sollte wenigstens eine Warnung rausspringen. Eine Referenz ist ungefähr so viel Zeiger, wie ein Hardlink eine symbolische Verknüpfung ist...


Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen



Wann benutzt man Pointer in C?

Pointer ermöglichen es, das Funktionen ihre beim Aufruf übergebenen Variablen verändern können. dynamische Verwaltung von Speicherplatz, memory managment, funktioniert immer über Pointer. wenn man sein Programm optimieren will, Geschwindigkeit, Speicherbedarf sind Pointer immer sehr beliebt.

Für was braucht man Pointer?

Damit du z.B. in einer Funktion ein ganzes Array beschreiben kannst und du nur ein Wert übergeben musst(Adresse des ersten Speicherplatzes). Oder um in einer Unterfunktion die Variable, die im main definiert ist, zu verändern. Pointer sind hilfreich, um globale Variablen zu vermeiden.

Was ist eine Referenz in C++?

Referenzen sind interne Zeiger auf Variablen. Sie werden also genau so verwendet wie gewöhnliche Variablen, verweisen jedoch auf das Objekt, mit dem sie initialisiert wurden. Die Zeigerverwendung wird vor dem Programmierer verborgen.