4 Funktionen mit bzw. ohne Referenz
-
Ich hab hier 4 Funktionen:
int plus1(int a) //call-by-value { return (a+1) % 10; } void plus2(int &a) { a = (a+1) % 10; } int plus3(int &a) { return a = (a+1) % 10; } int& plus4(int &a) { return a = (a+1) % 10; }Die erste Funktion verstehe ich. Was allerdings plus2, plus3 und plus4 machen, ist mir etwas schleierhaft. Bei Funktion plus2 könnte ich mir vorstellen, dass der Adressoperator in der Funktionsdeklaration die Adresse von a holt. Aber wenn an die Berechnung innerhalb der Funktion nur die Adresse weitergegeben wird, dann kann ja nix berechnet werden weil ja kein Zeiger bei der Berechnung zum Einsatz kommt, oder?
Könnt ihr das erklären?
-
plus2: Weist dem als Parameter übergebenen Wert der Funktion den Rest der Division ((Parameter + 1) / 10) zu.
plus3: Gibt den Rest der Division ((Parameter + 1) / 10) zurück. (Achtung, Parameter sollteconstsein.)
plus4: Undefiniertes Verhalten -> Referenz auf lokales Objekt zurückgegeben.
-
Bei plus2 und plus3 wird die Referenz auf a übergeben, das heisst wenn a sich in der Funktion ändert, ändert sich auch das a auch ausserhalb der Funktion. Du hast also keine lokale Kopie. Es wird dann aber eine Kopie des Werts zurückgebgeben.
Bei plus4 ist der einzige Unterschied, dass eine Referenz auf das a zurückgegeben wird.
Teste die Funktionen mal und übergebe verschiedene Werte und schau dir dann die Ergebnisse an. Dann siehst du was rauskommt.
*Edit
plus2 hat natürlich gar keinen Rückgabewert. Da du aber auf der Referenz von a operierst, ändert sich auch bei plus2 der Wert von a ausserhalb der Funktion.
-
Ist dann plus2 als call-by-reference anzusehen? Warum aber wird das dann hier mit dem &-Operator gemacht? Das erreicht man doch auch mit Zeigern, oder?
-
vip@r schrieb:
Ist dann plus2 als call-by-reference anzusehen? Warum aber wird das dann hier mit dem &-Operator gemacht? Das erreicht man doch auch mit Zeigern, oder?
Ja, das ist Call by Reference.
Man könnte es auch mit einem Pointer machen. Aber man verwendet für Parameter meistens Referenzen und keine Pointer. Natürlich gibt es da auch Ausnahmen.
-
Weil eine Referenz immer etwas gültiges referenziert und ein Zeiger auch mal irgendwo ins Nirvana zeigen kann.
-
vip@r schrieb:
Warum aber wird das dann hier mit dem &-Operator gemacht?
Du solltest dir das Thema Referenzen (nochmals) anschauen.
-
icarus2 schrieb:
Es wird dann aber eine Kopie des Werts zurückgebgeben.
Ich denke mal, dass beziehst du auf Funktion plus3, wo ja der berechnete Wert mittels return "zurückgegeben" wird, oder?
brotbernd schrieb:
Weil eine Referenz immer etwas gültiges referenziert und ein Zeiger auch mal irgendwo ins Nirvana zeigen kann.
Danke, das hat's durchsichtiger gemacht!
-
vip@r schrieb:
icarus2 schrieb:
Es wird dann aber eine Kopie des Werts zurückgebgeben.
Ich denke mal, dass beziehst du auf Funktion plus3, wo ja der berechnete Wert mittels return "zurückgegeben" wird, oder?
Genau. Habe ich *Edit noch erwähnt, dass plus2 gar keinen Rückgabewert hat. Hatte ich zuerst übersehen.
-
Durch eure Beiträge hab ich das jetzt so verstanden, dass Referenzen immer dann Anwendung finden können, wenn man z.B. Parameter an Funktionen übergeben will, von denen keine Kopie zurückgegeben werden braucht. Außerdem ist es (denke ich) oft einfacher, da es eben mit Pointern oft das Problem mit dem Erwischen des falschen Speicherbereichs gibt. Genau diese Gefahr ist anscheinend bei Referenzen nicht gegeben.
Ist das soweit richtig?
-
Grundsätzlich kann eine Referenz auch ins Nichts zeigen, z.B.:
int& f() { int bla = rand(); return bla; }Der Compiler erzwing allerdings, dass die Refernz mit etwas gültigem initalisiert wird und man muss (kann) auch nicht auf NULL prüfen.
-
Ich hab mir nun diesen kurzen Code geschrieben. Ich möchte nun das ganze mit Referenzen lösen. Ich hab schon versucht die Pointer-Sternchen gegen den &-Operator in der Parameterliste der Funktion auszutauschen, aber da meckert der Compiler dann... Geht das hier dann überhaupt?
#include<iostream> using namespace std; void addiere(int *a, int *b, int *c) { for(int i=0; i<5; i++) { c[i] = a[i] + b[i]; } } int main() { int a[5], b[5], c[5]; for(int i=0; i<5; i++) { a[i] = 1; b[i] = 1; c[i] = 0; } addiere(a, b, c); for(int i=0; i<5; i++) { cout << c[i]; } }
-
Man sollte nie ein lokales Objekt als Referenz zurückgeben.
-
EOutOfResources schrieb:
Mann sollte nie ein lokales Objekt als Referenz zurückgeben.
Ich denke, dass war an Oberon gemeint, oder? Ich geb ja kein lokales Objekt per Referenz zurück...
-
vip@r schrieb:
[...] Geht das hier dann überhaupt? [...]
Hier liegen die Dinge etwas anders. Wenn du ein Array übergibst, so hast du einen Pointer auf das erste Element des Arrays. Um dann auf die darauffolgenden Elemente zuzugreifen, brauchst du Pointerarithmetik. Heisst also, du musst zur Anfangsadresse etwas hinzuzählen, damit du das nächste Element erreichen kannst.
Das hier dürfte eine etwas genauere Erklärung sein: http://www.cplusplus.com/doc/tutorial/arrays/
Grundsätzlich solltest du aber keine rohe Arrays brauchen, sondern std::vector.
-
Stimmt, du hast Recht. Der []-Operator eines Arrays ist ja quasi nix anderes als ein Pointer. Dass hier dann die Referenz ins Gras beißt is ja klar; hätt ich selber auch drauf kommen können...
-
vip@r schrieb:
Der []-Operator eines Arrays ist ja quasi nix anderes als ein Pointer.
Nein, der Indexoperator ist vielmehr eine Addition und Dereferenzierung eines Zeigers.
p[i] == *(p+i)
-
Einfaches Modell: Stell Dir eine Refrenez einfach als eine Umbenennung des selben Objekts vor:
void funktion(irgendein_typ & r) { } //... irgendein_typ obj; //neues objekt mit namen 'obj' funktion(obj); //'obj' heisst in 'funktion' 'r', aber beide sind das gleiche objekt unter verschiedenen namen... irgendein_typ & renamed = obj; //ab hier kannst du 'obj' auch unter dem namen 'renamed' ansprechen std::cout << std::booalpha << (&obj == &renamed) << std::endl; //true -> gleiche adressen weil gleiches objekt nur mit verschiedenen namen
-
vip@r schrieb:
Stimmt, du hast Recht. Der []-Operator eines Arrays ist ja quasi nix anderes als ein Pointer. Dass hier dann die Referenz ins Gras beißt is ja klar; hätt ich selber auch drauf kommen können...
Lustigerweise ist das nicht so, das lässt sich auch per Referenz lösen.
Angenommen dein Array sieht so aus:`
+---+---+---+---+---+
| 0 | 1 | 2 | 3 | 4 |
+---+---+---+---+---+
`
dann kann man folgendes machen:
void f( int& a ) { // weist dem nächsten integer im Speicher den Wert 5 zu. Dazu wird die // Adresse der Variable bestimmt, per Zeigerarithmetik die Adresse des // nächsten Integers bestimmt und diesem Integer per Dereferenzierung der // Wert 5 zugewiesen. int* VarAddress = &a; VarAddress = VarAddress +1; *VarAddress = 5; } int main() { int a[5] = { 0 }; f( a[1] ); // setzt a[2] auf 5 f( *a ); // setzt a[1] auf 5 }Eine Referenz ist ein Alias für eine Variable, egal ob sie benannt ist oder nicht. Sie ist in jeder Hinsicht identisch mit dem Original.
int a = 5; int ar[5] = {0}; int& RefA = a; // RefA und a sind dieselbe Variable int& RefAr = ar[2] // RefAr und a[2] sind dieselbe Variable
-
Ich möchte auf zwei Punkte eingehen:
& und Referenzen: Da ein C++-Programm mit einem begrenzten Satz an Zeichen geschrieben wird und keiner eine bessere Idee zur Deklaration von Referenzen hatte, wurde die Bedeutung von & abermals überladen. Je nach Kontext ist es entweder der unäre Adress-Operator, ein "Referenz-Deklarator", oder ein binäres Und.
Die Parameterübergabe an eine Funktion ist auch nur eine "Kopier-Initialisierung". Soll heißen:
void foo(int x1, int & y1); int main() { int z = 3; foo(z+1,z); int x2 = z+1; int & y2 = z; }...dass die Initialisierung von x1 beim Funktionsaufruf genau dieselbe ist wie die von x2. Dasselbe gilt für y1 und y2. Wenn du verstanden hast, was y2 ist, verstehst Du auch, was y1 ist -- eine Referenz auf ein int-Objekt. Das & ist hier jeweils Teil des Deklarators und macht aus y1 bzw y2 eine Referenz. Was eine Referenz ist, kann man auch in einem C++ Buch nachlesen...
Ich stelle mir eine Referenz immer als einen sich selbst dereferenzierenden Zeigers vor:
int z = 3; int z = 3; int &ref = z; int *ptr = &z; ref += 1; *ptr += 1; cout << z; cout << z;Links siehst Du & einmal als Teil des Deklarators. Er macht aus ref eine Referenz. Rechts siehst Du & als Adress-Operator. Er liefert die Adresse von z, mit der der Zeiger ptr initialisiert werden kann. Das erste Sternchen ist Teil des Deklarators, der aus ptr einen Zeiger macht. Das zweite Sternchen ist der Dereferenzierungs-Operator. Der ganze Ausdruck *ptr bezieht sich wieder auf das Objekt mit der Adresse, die in ptr gespeichert wird.