Parameter passing and references

In C and C++ parameters are passed call by value, i.e. they are evaluated first, then copied, and the function that changes the value of one of the parameters changes its own copy but not a variable of the caller.

This can be demonstrated using a simple example:

#include <printf.hpp> /* needed for fmt::printf */

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

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

We have the same behaviour for composite objects like a struct. The transpose function has no effect in the following example:

#include <cstddef> /* needed for std::size_t and std::ptrdiff_t */
#include <utility> /* needed for std::swap */
#include <printf.hpp> /* needed for fmt::printf */

struct Matrix {
   std::size_t m; /* number of rows */
   std::size_t n; /* number of columns */
   std::ptrdiff_t incRow;
   std::ptrdiff_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 * m + i + 1;
         }
      }
   }

   void print() {
      for (std::size_t i = 0; i < m; ++i) {
         fmt::printf("  ");
         for (std::size_t j = 0; j < n; ++j) {
            fmt::printf(" %4.1lf", data[i*incRow + j*incCol]);
         }
         fmt::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();
   fmt::printf("A =\n"); A.print();
   transpose(A);
   fmt::printf("A^T =\n"); A.print();
}

In C++, std::swap is one of the standard functions that swaps efficiently the values of two variables of the same type.

When we want to give a function (or method) the opportunity to change the value of a variable that is passed to it, we have to pass the variable's address as pointer instead of the variable's value. Then updates are possible:

#include <printf.hpp> /* needed for fmt::printf */

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

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

In this example, f has to be adapted significantly. Everywhere, were we before used i to access the variable's value, we need to replace this by *i as i became a pointer which needs to be dereferenced first before the actual value can be accessed.

In C++, this can be done more elegantly using references. A reference is an alias for another object using a hidden pointer. Hidden means that we do not have to dereference this pointer explicitly as before. As these hidden pointers are tied permanently to the object they refer to, we cannot change the hidden pointers.

#include <printf.hpp> /* needed for fmt::printf */

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

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

It is remarkable that the only difference between this version and the original example is that we have changed the parameter declaration from int val to int& val. Thereby val in f became a reference, and when f(i) is invoked, val is bound to i.

theon$ diff -U 3 call1.cpp call4.cpp
--- call1.cpp	Mon Nov  5 11:18:54 2018
+++ call4.cpp	Mon Nov  5 11:18:54 2018
@@ -1,6 +1,6 @@
 #include  /* needed for fmt::printf */
 
-void f(int val) {
+void f(int& val) {
    val = val + 1;
 }
 
@@ -7,5 +7,5 @@
 int main() {
    int i = 1;
    f(i);
-   fmt::printf("i = %d\n", i); /* still 1 */
+   fmt::printf("i = %d\n", i); /* now 2 */
 }
theon$ 

Exercise

How to adapt transpose in the example above such that it is no longer without effect?