Content

Parameterübergabe und Referenzen

Sowohl in C als auch C++ werden Parameter grundsätzlich per call by value übergeben, d.h. sie werden kopiert und die Funktion, die einen Parameter verändert, ändert damit ihre private Kopie und nicht die entsprechende Variable des Aufrufers.

Dies lässt sich an einem einfachen Beispiel demonstrieren:

#include <cstdio>

void f(int val) {
   val = val + 1;
}

int main() {
   int i = 1;
   f(i);
   std::printf("i = %d\n", i); /* still 1 */
}

Die gleiche Verhaltensweise haben wir auch bei kompositen Objekten wie bei einer struct. Die Funktion transpose in dem folgenden Beispiel hat entsprechend keinen Effekt:

#include <cstddef> /* needed for std::size_t */
#include <cstdio> /* needed for std::printf */
#include <utility> /* needed for std::swap */

struct Matrix {
   std::size_t m; /* number of rows */
   std::size_t n; /* number of columns */
   std::size_t incRow;
   std::size_t incCol;
   double* data;

   void init() {
      for (std::size_t i = 0; i < m; ++i) {
         for (std::size_t j = 0; j < n; ++j) {
            data[i*incRow + j*incCol] = j * n + i + 1;
         }
      }
   }

   void print() {
      for (std::size_t i = 0; i < m; ++i) {
         std::printf("  ");
         for (std::size_t j = 0; j < n; ++j) {
            std::printf(" %4.1lf", data[i*incRow + j*incCol]);
         }
         std::printf("\n");
      }
   }
};

void transpose(Matrix A) {
   std::swap(A.m, A.n);
   std::swap(A.incRow, A.incCol);
}

int main() {
   Matrix A;
   A.m = 7; A.n = 8;
   A.data = new double[A.m * A.n];
   A.incRow = 1; A.incCol = 7;
   A.init();
   std::printf("A =\n"); A.print();
   transpose(A);
   std::printf("A^T =\n"); A.print();
}

In C++ gehört std::swap zu den Standardfunktionen, mit denen die Werte zweier Variablen effizient ausgetauscht werden können.

Wenn wir in C einer Funktion die Möglichkeit geben wollten, den Wert einer übergebenen Variable zu verändern, haben wir nicht den Wert, sondern einen Zeiger auf die Variable übergeben. Dann ist auch eine Änderung möglich:

#include <cstdio>

void f(int* val) {
   *val = *val + 1;
}

int main() {
   int i = 1;
   f(&i);
   std::printf("i = %d\n", i); /* now 2 */
}

Hier hat sich die Funktion f deutlich verändert. Überall, wo zuvor i stand, musste dies durch *i ersetzt werden, d.h. der Zeiger musste jeweils dereferenziert werden.

Dies lässt sich in C++ eleganter mit Referenzen lösen. Eine Referenz ist ein Alias für ein anderes Objekt mit einem impliziten Zeiger darauf. Implizit heisst hier, dass wir Referenzen nicht explizit dereferenzieren müssen wie Zeiger. Der implizite Zeiger kann nicht verändert werden, d.h. eine Referenz wird bei der Erzeugung mit genau einem Objekt dauerhaft verbunden.

#include <cstdio>

void f(int& val) {
   val = val + 1;
}

int main() {
   int i = 1;
   f(i);
   std::printf("i = %d\n", i); /* now 2 */
}

Es ist beachtenswert, dass der einzige Unterschied zwischen dieser Fassung und dem Ausgangsbeispiel darin besteht, dass bei f die Parameterdeklaration int val durch int& val ersetzt worden ist. Somit ist val in f eine Referenz. Beim Aufruf von f(i) wird die Referenz von val an die Variable i gebunden.

Aufgabe

Wie kann transpose in dem obigen Beispiel so angepasst werden, dass es funktioniert?