Interview Questions: Call by reference and call by value

A common simple interview question is to explain the difference between “call by value” and “call by reference”. This refers to the way arguments are passed to functions.

In call by value, a copy of the argument is made and given to the function so that any changes made to the argument affect only the function itself. Once the function returns, the original variable passed as the argument is unchanged.

void foo ( int x ) {
    x = x + 1;
    printf( "In foo x is now %d\n", x );
}

int a = 0;
printf( "Before foo a is %d\n", a );
foo( a );
printf( "After foo a is %d\n", a );

 

This code snippet will produce the following output:

Before foo a is 0
In foo x is now 1
After foo a is 0

The value of a is unchanged because a is passed by value, and in the function foo(), x merely holds a copy of the value of a so that changes to x do not affect a.

Call by reference does things differently, however. In call by reference, the function argument refers to the exact same variable as the one passed by the caller, so changes made in the function are visible to the caller.

void foo ( int &x ) {
    x = x + 1;
    printf( "In foo x is now %d\n", x );
}

int a = 0;
printf( "Before foo a is %d\n", a );
foo( a );
printf( "After foo a is %d\n", a );

 

This code snippet will produce the following output:

Before foo a is 0
In foo x is now 1
After foo a is 1

Notice the subtle change to the definition of foo(). Now the argument is defined as “int &x“. In C++ this tells the compiler that the argument is a reference parameter instead of a value parameter. Therefore x becomes an alias for a when we call foo()

Different languages support different conventions. C++, for example, supports both call by value (the default) and call by reference (when you define an argument as a reference parameter as I did in the second example above). C, on the other hand, is purely call by value. To achieve the effect of call by reference in C you have to declare your argument as a pointer to something, in which case the pointer is passed by value, but you can dereference the pointer to change the thing it points at.

void foo ( int *x ) {
    *x = *x + 1;
    printf( "In foo *x is now %d\n", *x );
}

int a = 0;
printf( "Before foo a is %d\n", a );
foo( &a );
printf( "After foo a is %d\n", a );

 

Before foo a is 0
In foo x is now 1
After foo a is 1

Again, this is actually an example of call by value – x contains a memory address that is a copy of the address of a and if you change x then you are only changing what x points to – a is unchanged, and so is it’s address in memory. Some people refer to this idiom as “call by pointer”, though in my opinion that is really a misnomer since the language is not actually providing you with another calling convention, rather you are simply using a programming idiom to achieve the affect you want.

There are languages that are neither call by reference nor call by value. Python, for instance. You can read a nice writeup about that in “Is Python call-by-value or call-by-reference? Neither.

Perl is an interesting case to me in that it is actually a call by reference language, but the most idiomatic way of using it makes it seem like it is call by value. Consider the following sample.

sub foo {
    my $x = shift;
    $x = $x + 1;
    print "In foo $x\n";
}

my $a = 0;
print "Before foo $a\n";
foo($a);
print "After foo $a\n";

 

This produces the following output, which makes it look like Perl is passing the argument by value.

Before foo 0
In foo 1
After foo 0

But what is really going on is that you, the programmer, are making a copy of the argument as the first thing in your function. The line my $x = shift; actually makes a copy of the first element of the @_ argument array (and removes the first element afterwards). Look what happens when you avoid the shift:

sub foo {
    $_[0] = $_[0] + 1;
    print "In foo ".$_[0]."\n";
}

my $a = 0;
print "Before foo $a\n";
foo($a);
print "After foo $a\n";

 

Before foo 0
In foo 1
After foo 1

Now it is clear that Perl is really passing the variable $a by reference.