Skip to content

Der Unterschied zwischen call-by-value und call-by-reference

Byte Order Mark

Es gibt verschiedene Möglichkeiten an eine Funktion bzw. Methode Parameter zu übergeben. Bekannt sind call-by-value (CBV) und call-by-reference (CBR). Ich möchte kurz auf die Unterschiede eingehen und welche Möglichkeiten der Parameterübergabe verschiedene Programmiersprachen bieten.

call-by-value

Bei CBV werden die an eine Funktion übergebenen Daten kopiert. Es besteht dann keine Verbindung mehr zwischen den Daten beim Aufrufer und den Daten in der Funktion. Werden große Objekte auf diese Weise übergeben, so ist das sehr kostspielig in Bezug auf Rechenzeit (der Kopiervorgang) und Speicherplatz (Daten sind mehrmals vorhanden).

call-by-reference

Anstatt die Daten zu kopieren werden Referenzen auf die Daten übergeben. Man kann sich das als Zeiger vorstellen: Beim Aufrufer zeigt eine Variable auf ein bestimmtes Datum, in der Funktion zeigt eine andere Variable auf dasselbe Datum. Methodenaufrufe an einem so übergebenen Objekt arbeiten also auf demselben Objekt, das auch außerhalb sichtbar ist.

Beispiele

Anhand der Programmiersprachen C++, Java und Python soll das Konzept verdeutlicht werden.

C++

In C++ kann der Programmierer angeben, auf welche Art ein Datum übergeben wird:

void f( int x ){
  x = x * 2;
}

Hier wird das Datum call-by-value übergeben. x enthält eine Kopie des Werts, der durch den Aufrufer übergeben wurde. Eine Änderung von x innerhalb der Methode ist beim Aufrufer nicht sichtbar.

void g( int& x ){
  x = x * 2;
}

Hier wird das Datum per Referenz übergeben. Dafür sorgt der Referenzoperator &. x zeigt nun auf dasselbe Objekt (in diesem Fall einen Integer), auf das auch der Aufrufer zugreift. Mehr noch, x ist dieselbe Referenz, die auch dem Aufrufer zur Verfügung steht. Dadurch ist eine Änderung der Referenz (d.h. ein ‘Umbiegen’ auf ein anderes Objekt) auch beim Aufrufer sichtbar. Dies ist beachtenswert, weil das in Java und Python nicht der Fall ist. Dazu später mehr.

void h( int* x ){
  (*x) = (*x) * 2;
}

Der vorangehende Code stellt dieselbe Funktionalität wie der Code mit dem Referenzoperator dar, implementiert diese jedoch mit Hilfe von Zeigern. Es ist offensichtlich, dass die Umsetzung mit Zeigern weniger komfortabel ist als die mit Referenzen. Da es den Referenzoperator allerdings nur in C++ und nicht in C gibt, bietet der Einsatz von Zeigern die Möglichkeit ein call-by-reference in C zu realisieren.

Java

Java ist irgendwie ‘schwieriger’ was die Parameterübergabe angeht. Andererseits auch ganz einfach: In Java werden Parameter immer per call-by-value übergeben…

void f( int x ){
  x = x * 2;
}

Da der Wert von x CBV übergeben wurde, bekommt der Aufrufer von einer Veränderung nichts mit.

In Java ist zwischen primitiven Datentypen und Objekttypen zu unterscheiden. Wie sieht es also mit folgendem Code aus?

void g( SomeObject x ){
  x = new SomeObject()
}

Auch hier bekommt der Aufrufer nichts von der Änderung mit. Wieso? Das liegt daran, dass x zwar eine Referenz auf ein Objekt ist, diese Referenz allerdings per call-by-value übergeben wurde. D.h. sie wurde kopiert. Bei der Parameterübergabe hat man also zwei Referenzen, die auf dasselbe Objekt zeigen. Im Gegensatz zu einer Referenz (in zwei Variablen), die auf dasselbe Objekt zeigen, im Falle von C++ und dem Referenzoperator.

Bei einer Neuzuweisung eines Wertes an eine Variable verhält sich Java also bei primitiven Datentypen und Objekttypen gleich. Praktisch. Aber…

void h( SomeObject x ){
  x.doSomethingWithSideEffect()
}

Hier wird eine Methode auf dem Objekt aufgerufen, das von x referenziert wird. Da Java Parameter immer per CBV übergibt, ist die Referenz, die x speichert, eine Kopie. Allerdings zeigt sie auf dasselbe Objekt wie beim Aufrufer. Hat der Methodenaufruf Seiteneffekte auf das Objekt (wir nehmen das mal an), dann können diese Seiteneffekt beim Aufrufer wahrgenommen werden. Natürlich merkt der Aufrufer in nachfolgendem Code wiederum nichts von den Seiteneffekten, da die Referenz vor dem Methodenaufruf ‘entkoppelt’ wird:

void i( SomeObject x ){
  x = new SomeObject()
  x.doSomethingWithSideEffect()
}

Also: In Java werden Parameter immer per call-by-value übergeben. Auch Referenzen. Bei veränderbaren Objekten sind Veränderungen in der Methode auch beim Aufrufer sichtbar. Bei unveränderbaren Typen, wie beispielsweise primitiven Datentypen und Strings, merkt der Aufrufer nichts. Bei solchen unveränderlichen Typen könnte man eine Veränderung durch einen Wrapper nach außen tragen. Das ist vom Design her allerdings wahrscheinlich keine gute Lösung…

Python

Python verhält sich genau wie Java. Im Prinzip. Aber: In Python wird auf alle Daten mit Referenzen zugegriffen. Auch primitive Datentypen. Und: In Python ist viel einfacher festzustellen, was vor sich geht:

>>> def f(x):
...     x = x * 2
...
>>> s = 5
>>> l = ['o']
>>> f(s), f(l)
(None, None)
>>> s
5
>>> f(s)
>>> s
5
>>> f(l)
>>> l
['o']

Es sollte klar werden, dass auch Python call-by-value einsetzt. Dass Referenzen übergeben werden zeigt die folgende Interaktion mit dem Python-Interpreter:

>>> def g(x):
...     x.append('la')
...
>>> g(l)
>>> l
['o', 'la']

Bei veränderlichen Objekten schlägt sich eine Änderung innerhalb einer Methode also zum Aufrufer durch. Wie zu erwarten. Bei CBV mit Referenzen…

Ich möchte noch auf einen Punkt hinweisen. Das Python-Beispiel zeigt, wie hilfreich, nützlich und vor allem lehrreich das Arbeiten mit einem interaktiven Interpreter sein kann. Eine solche Interaktion liefert unmittelbares Feedback, das didaktisch sehr viel bringt. Und damit zurück zur Parameterübergabe…

{ 14 } Comments

  1. cb | 2007/2/21 at 11:47 | Permalink

    guter Artikel , gut erklärt ! thnx

  2. Markus | 2007/5/13 at 08:48 | Permalink

    Sehr vielen Dank! :) Guter Artikel!

  3. fankmann | 2007/12/7 at 05:06 | Permalink

    Super Erklärung echt Spitze
    hat mir echt weitergeholfen
    Danke

  4. Prosurferin | 2008/1/13 at 12:52 | Permalink

    Jo, ist logisch + sehr gut erklärt!

  5. Noor | 2008/1/18 at 10:08 | Permalink

    Sehr verständlich dargestellt und gute Beispiele. Vielen Dank!

  6. Sebastian | 2008/9/7 at 03:42 | Permalink

    Super Erkärung =) hab grade für meine Informatik Klausur am Dienstag nachgeguckt :P

  7. Hauser Roman | 2008/9/8 at 11:39 | Permalink

    Ich wollte nur kaffee trinken.. java.. aber hey was ist passiert? call by völiu.. ?
    nun ja.. schönen tag

  8. Michael | 2008/11/13 at 11:32 | Permalink

    Super Artikel!, danke, gruss

  9. sporedjack | 2009/6/8 at 06:41 | Permalink

    Danke, sehr gut erklärt!

  10. Hasan | 2009/6/11 at 05:46 | Permalink

    Hey, vielen Dank. Hat mir weitergeholfen ;) bye

  11. Luitzifa | 2010/6/20 at 11:03 | Permalink

    und selbst 5 jahre später hilft dieser artikel noch menschen wie mir…
    danke!

  12. buschflitzer | 2010/10/28 at 12:23 | Permalink

    Sehr gut erklärt.
    Toller Artikel, weiter so!!

  13. Horst | 2010/12/22 at 08:33 | Permalink

    Danke, gefällt mir !

  14. Ich | 2011/8/20 at 07:19 | Permalink

    Danke!

Post a Comment

Your email is never published nor shared. Required fields are marked *