Geschrieben von: Robert Mertens | Letztes Update: 

Call-by-Value und Call-by-Reference bei der Parameterübergabe

Wenn du einer Funktion Parameter übergibst, solltest du dir darüber Gedanken machen, ob dies als Wertparameter (call-by-value) oder als Referenzparameter (call-by-reference) geschieht. Gleichermaßen solltest du dir beim Schreiben einer Funktion überlegen, welche Art der Parameterübergabe für deinen Zweck am besten ist. Dieser Artikel erklärt dir den Unterschied zwischen call-by-value und call-by-reference und gibt dir praktische Tipps für deren Verwendung.

Call-by-Value – Wertparameter

In vielen Programmiersprachen werden im Normalfall Parameter an Funktionen mithilfe einer Kopie übergeben. Das wird als call-by-value bezeichnet. Das bedeutet, dass innerhalb der aufgerufenen Funktion mit der Kopie gearbeitet wird und sich Änderungen nicht auf den ursprünglichen Wert auswirken.

Definition einer Funktion mit Wertparametern in C++


// Addiere a und b.
int CHelper::Sum(int a, int b)
{
// Sowohl 'a' als auch 'b' werden "by value" übergeben
return a + b;
}

Call-by-Reference – Referenzparameter

Bei einer Übergabe als Referenz wirken sich Änderungen an den Parametern auf die Ursprünglichen Werte aus. In der Praxis wird das zum Beispiel verwendet, wenn eine Funktion mehrere Rückgabewerte hätte. Nachfolgend ein Beispiel für eine Funktion, die call-by-reference nutzt:

Definition einer Funktion mit Referenzparametern in C++


void CHelper::Swap(int& a, int& b)
{
// Sowohl 'a' als auch 'b' werden "by reference" übergeben,
// deshalb ist eine Zuweisung möglich.
int temp = a;
a = b;
b = temp;
}

Die Funktion Swap tauscht die Inhalte der Variablen. Damit sie das auch machen kann, müssen die Werte per Referenz übergeben werden, ansonsten hätte die Funktion gar keinen Einfluss auf die Ursprünglichen Variablen.

Kopie einer Referenz

Wenn es sich bei Funktionsparametern um Objekte handelt, ist es in vielen Programmiersprachen üblich, dass für diese Objekte lediglich Referenzen oder Zeiger übergeben werden, also nur die Adresse an der das jeweilige Objekt gespeichert ist. Kommt in so einem Fall call-by-value zum Einsatz wird eben nicht das Objekt kopiert, sondern das Element, dass die Speicheradresse des Objekts enthält. Referenzen können in C++ nicht kopiert werden, Pointer hingegen werden standardmäßig kopiert, wie das nachfolgende Beispiel verdeutlicht:

Übergabe eines Pointers by-value


void PrintAndDeleteMyObject(CExampleClassBase* object)
{
// Print() vom übergebenen Objekt wird ausgeführt
object->Print();

// Lösche das Objekt
delete object;

// Der Zeiger wird hier nur lokal zurückgesetzt,
// für den Aufrufer der Funktion ändert sich nichts.
// D.h. es steht im ursprünglichen Zeiger noch
// immer die Adresse des gelöschten Objektes drin,
// was zu Fehlern führen kann.
object = NULL;
}

Möchtest du den ursprünglichen Zeiger der Funktion manipulierbar übergeben, kannst du den Pointer mit dem &-Operator auch als Referenz übergeben:


void PrintAndDeleteMyObject(CExampleClassBase*& object)
{
// Print() vom übergebenen Objekt wird ausgeführt
object->Print();

// Lösche das Objekt
delete object;

// Der Zeiger des Aufrufers wird jetzt auf NULL
// zurückgesetzt. Dies funktioniert nur, weil
// der Zeiger als Referenz übergeben wurde.
object = NULL;
}

Einen Zeiger per Referenz zu übergeben wird in der Praxis so gut wie nie benötigt. Du solltest vor der Verwendung von solchen Konstrukten gut überlegen, ob es nicht einen einfacheren, besseren Weg gibt.

Call-by-Value und Call-by-Reference in C++

In C++ ist so wie in vielen anderen Programmiersprachen call-by-value der Normalfall. Es werden also sowohl Pointer als auch normale Werte bei der Übergabe an Funktionen kopiert. Durch die Verwendung des &-Zeichens kannst du call-by-reference erzwingen und damit eine Kopie vermeiden. Objekte werden häufig mit einem Pointer oder per Referenz übergeben, da meistens keine Kopie des Objektes gewünscht ist.

Übergibst du ein Objekt das nicht geändert werden soll per Referenz, kannst du es mit dem Schlüsselwort const als konstant und damit “read-only” markieren. Eine möglichst umfangreiche Verwendung von const nennt man const-correctness, eine fortgeschrittene Programmiertechnik in C++ mit der Fehler vermieden werden können.

Es kann auch einmal vorkommen, dass es nötig ist, die Adresse eines Pointers von einer Funktion manipulieren zu lassen. In diesem Fall kannst du den Pointer per Referenz übergeben (siehe obiges Beispiel).

Call-by-Value und Call-by-Reference in Java

In Java gibt es ausschließlich call-by-value. Das bedeutet, dass bei Werttypen (primitive Datentypen wie beispielsweise Integer) die Werte und bei Referenztypen (Objekte) die Referenzen auf Objekte kopiert werden. Nochmal langsam: wenn du einen Integer, Float o.Ä. übergibst, wird der Wert kopiert und an den ursprünglichen Werten des Aufrufers kann nichts mehr geändert werden. Wenn du ein Objekt übergibst, wird die Referenz kopiert. Das bedeutet aber, dass noch immer das gleiche Objekt referenziert wird, Änderungen darauf wirken sich also sehr wohl auf das ursprüngliche Objekt aus.

Wertparameter und Referenzparameter in Java


public void printData(int wertParameter, Object referenzParameter)
{
System.out.print(wertParameter);
System.out.print(referenzParameter);
return;
}

Beide Parameter von printData werden kopiert. Zuweisungen an wertParameter würden sich nur innerhalb der Funktion auswirken. Änderungen an dem Objekt auf das referenzParameter zeigt, würden sich auch nach außen hin auswirken, da ja das Objekt und nicht die kopierte Referenz geändert werden würde.

Call-by-Value und Call-by-Reference in C#

Java und C# sind sich ja als Programmiersprachen sehr ähnlich. Auch bei der Parameterübergabe unterscheiden sie sich nicht großartig. Standardmäßig wird auch in C# by-value übergeben, bei Werttypen werden die Werte kopiert, bei Referenztypen die Referenzen. Allerdings bietet C# auch noch die Möglichkeit call-by-reference zu erzwingen. Dazu gibt es die Schlüsselwörter ref und out.

call-by-value in C#

public void swapContent(ref int a, ref int b)
{
int temp = a;
a = b;
b = temp;
}

Durch das Schlüsselwort ref wird bei der Parameterübergabe call-by-reference erzwungen und Änderungen an den Parametern wirken sich auch auf die ursprünglichen Werte aus.

Fazit

Der Unterschied zwischen call-by-value und call-by-reference kann den Programmverlauf deutlich beeinflussen. Damit du gute Programme schreiben kannst, solltest du dir also immer im Klaren sein, was bei einem Funktionsaufruf mit deinen Parametern genau passiert. Am besten öffnest du gleich die Entwicklungsumgebung deiner Wahl und probierst die verschiedenen Möglichkeiten im Debugger aus.

FAQ: Häufige Fragen zu Call-by-Value und Call-by-Reference

Was ist der Unterschied zwischen Call-by-Value und Call-by-Reference?

Call-by-Value übergibt eine Kopie der Variablen, während Call-by-reference die Variable übergibt.

Bei Call-by-Value werden tatsächliche und formale Argumente an verschiedenen Stellen erzeugt, während bei Call-by-Reference tatsächliche und formale Argumente an der gleichen Stelle erzeugt werden.

Was bedeutet Call-by-Value?

Call-by-value ist eine Methode, die Argumente an Funktionen übergibt. Dabei wird der tatsächliche Wert in den formalen Parameter kopiert. In der C-Programmierung wird standardmäßig Call-by-Value für die Übergabe von Argumenten verwendet. Das bedeutet, dass der Code innerhalb einer Funktion die übergebenen Argumente nicht ändern kann.

Was ist Call-by-Value in Java?

Die Java-Methode, eine Methode durch einen Wert aufzurufen, wird call-by-value genannt. In diesem Fall werden Kopien der Werte an die Parameter übergeben, anstatt der eigentlichen Werte. Eine Variable ist ein Verweis auf eine Speicheradresse. Dies ist allgemein bekannt.