Values and references in modern programming languages

New programmers are often confused about the differences between values and references. This document is an attempt to clarify that, and show that it's really not that difficult -- and that most modern object-oriented languages deal with these things in pretty much the same way.

This document uses Java, REALbasic, C++, and Python as its example languages, because those happen to be the ones I know best. But similar principles apply to many other modern languages, including VB.NET, C#, and so on. If you know any language taught in the last decade or two, chances are good that the principles below will apply to it.

Variables that Contain Simple Values

The simplest case is to consider variables that hold a simple value, such as a number. In most languages, the variable name is mapped (by the compiler or interpreter) directly to a memory location where the numeric value is stored. Here's an example in several languages:

JavaREALbasicC++Pythonresult
x = 42;
y = 81;
x = 42
y = 81
x = 42;
y = 81;
x = 42
y = 81

The value of a simple type is the data itself.

The situation in Python is actually a bit more complex than this, but we'll come to that in a moment. In the other languages, though, the result of the two assignment statements is as shown: we simply have the value 42 stuffed into the memory location we call "x", and the value 81 stuffed into "y". Note that this memory location may be on the call stack, if it is a local variable within a function, or it may be in global storage. It looks the same either way; the only thing special about local variables is that space is allocated for them anew every time the function is called, and that space is then reclaimed when the function exits.

Variables that Contain Object References

That works fine for simple values like 42, but what if the data you need to pass around is something more complex, like a complete name and address? In this case, you would declare a class to represent that complex data type; each object of that class would represent one name and address. But that's a lot of data to be allocating and freeing every time a function is invoked. So instead, the storage for the actual object data goes in the global area, and what's stored in the variable itself is only a reference to that data. It looks like this:

It sounds obvious, but it's worth spelling out:

The value of an object reference is a reference to the object.

The value of the variable would actually be something like the memory address where the data is stored, or some other unique internal identifier that allows the data to be looked up when needed. That look-up is usually done via the "dot" operator, which tells the compiler that instead of the value of the reference itself, you want to "dereference" it and locate some item of data on the object to which it refers. For example, here's how you would access the "name" field of the object referred to by "x" in the diagram above.

JavaREALbasicC++Pythonresult
x.name
x.name
x->name
x.name
"Bob"

Easy, right?

Assignment Statements

Now that we have a good idea of what a variable actually contains — either a simple value, or an object reference — we can look at what happens when you assign one value to another. It's simple: the value on the right-hand side is simply copied over the value on the left-hand side. Suppose we start with x = 42, and y = 81, as shown in the first diagram above, and then assign y to x:

JavaREALbasicC++Pythonresult
y = x;
y = x
y = x;
y = x

Nothing mysterious here.

Assignment copies a value into a variable.

But what if x and y were object references? It turns out that exactly the same thing happens: the value of y is copied over the value for x. Just keep in mind that the values here are object references, not the data stored in the objects themselves. So after doing the same "y = x" assignment with object references, we would have:

Before, x and y referred to two different objects; after the assignment they refer to the same object. Again, there's nothing mysterious going on; it's just that the value of these variables are references to objects stored somewhere else.

This does have some interesting implications, however. Mainly, it means that you can have many variables that all refer to the same object. If we were to change Bob's zip code to 91234, then it doesn't matter which reference you use to do that — or which reference you use to check the data later. If all references are pointing to the same data, then of course a change made by one of them is immediately visible from all others.

One last point before we move on. What happens to the "Mary" object above, that no longer has any references to it? Modern languages will usually take care of disposing of that data storage automatically, through some garbage collection algorithm such as reference counting. C++, however, does not do this; that will simply leak the object unless you remember to explicitly free that data before losing your last reference to it.

Call by Value

Now let's consider what happens when you call a function that takes one or more parameters. Usually, the argument (i.e. the value passed into the function) can be not just a simple variable, but an expression. When your program reaches the point in the code where the function is invoked, the expressions will be evaluated, stored on the call stack, and become local variables from the point of view of the function being called.

But what if you're passing just a simple variable? In most cases, that is just a trivial expression, and is treated as such: it is "evaluated" (its value fetched from wherever that variable is stored), and the value is copied onto the call stack, to become a local variable in the called function. This is called call-by-value.

To make it more concrete, here are some simple function declarations and invocations in our example languages, where "x" is a reference to an object of class "Person":

JavaREALbasicC++Python
void Foo(Person p)
{
    ...
}

...
Foo(x);
Sub Foo(ByVal p As Person)
...
End Sub

...
Foo x
void Foo(Person* p)
{
    ...
}

...
Foo(x);
def Foo(p):
    ...

Foo(x)

(Note: the "ByVal" in the REALbasic example is not required, and not normally used, as it is the default mode. But it can be included to be explicit.)

You can think of call-by-value as being the same as assignment. The local variable (within the called function) is assigned the value you pass in (which may happen to be a global or local variable in the calling code).

So what happens if the method called tries to change the value of its parameter — assigning a new value to "p" in the examples above? Some languages won't allow this, but these languages all will. And it's no big deal; "p" is simply a local variable within the Foo method. Assigning a new value to it makes p refer to something else, but this has no effect whatsoever on the "x" variable that was passed in. Those are two different variables, and they can have two different values.

Parameters passed by value can never be changed by the called function.

Of course, if you don't assign a new value to p, then while Foo is executing we have p and x referring to the same object. A change in the data of that object (for example, renaming the person or changing their zip code) will naturally be seen by the calling code when the function returns. This is no different from any other two variables that happen to refer to the same object.

(The particular case of call-by-value where the values are object references is sometimes referred to as "call by sharing", to emphasize that the object is "shared" between the caller and the method. Oddly, nobody uses "assignment by sharing" even though assignment results in exactly the same thing, as shown above.)

Call by Reference

Some languages allow for a variation on function parameters known as call-by-reference. This is quite different from call-by-value. With call-by-reference, the parameter to the method is not a local variable at all, but actually an alias of the variable passed in. That is, we have two names for the exact same variable.

This means that anything you do to the variable within the function, is also done to the variable in the calling code — including assignment of a new value. Here's what that might look like:

JavaREALbasicC++Python
(not possible)
Sub Foo(ByRef p As Person)
   p = p.Parent
End Sub

...
Foo x
void Foo(Person* &p)
{
    p = p->Parent;
}

...
Foo(x);
(not possible)

Java and Python always pass by value, and have no option for passing by reference. But REALbasic and C++ do; after passing "x" into Foo as shown above, the value of x (that is, what object it refers to) has actually changed, so that it now refers to the Parent of its former value. (VB.NET has this feature too, as explained here.)

Parameters passed by reference may be assigned new values by the called function.

Note that this would work just the same with simple value types as well. Suppose you wanted a method to square a number:

JavaREALbasicC++Python
(not possible)
Sub Square(ByRef i As Integer)
   i = i*i
End Sub

...
Square x
void Square(int &i)
{
    i = i*i;
}

...
Square(x);
(not possible)

In this case, after calling Square, the value of x will be the square of its former value.

Call-by-reference has one big limitation: because the parameter becomes just an alias of a variable stored somewhere else, it is not useful (and usually not allowed) to pass in a complex expression or literal value as the argument. Only simple variables may be used for by-reference parameters.

A more subtle problem with call-by-reference is that it may surprise the reader of the code. Because most languages default to (and in many cases, only support) call-by-value, we expect that passing a variable into a function will not change the value of that variable. With call-by-value, that's always true, but with call-by-reference it may not be. Thus, call-by-reference may introduce unexpected side effects. However, it can be handy in cases where its use is clear, such as a method for swapping the values of two variables:

JavaREALbasicC++Python
(not possible)
Sub Swap(ByRef n1 As Integer, ByRef n2 As Integer)
   Dim temp As Integer = n1
   n1 = n2
   n2 = temp
End Sub
...
Swap x, y
void Swap(int &n1, int &n2)
{
	int temp = n1;
	n1 = n2;
	n2 = temp;
}
...
Swap(x,y);
(not possible)

Yes, This Applies to Python Too

All modern OOP languages have pretty much the same call semantics (the only variation being whether or not the language allows for call-by-reference), and most of the communities around those languages are perfectly content using the standard terminology for them.

But in the Python community, for some reason, there are many who want to insist that Python's semantics are neither call by reference, nor call by value, but something different. And they make up new terms or borrow terms made up by even more obscure language communities (like Common LISP), like "call by object". Even when you show, step by step, that call-by-value and call-by-reference apply perfectly well to object references, and that the semantics in Python are no different from those in languages (like VB.NET and REALbasic) that explicitly call these modes ByVal and ByRef, they insist that somehow in Python, it's something new and different.

In many cases this seems to be motivated by a desire to avoid the concept of object references, claiming instead that a Python variable holds an object directly. This despite the Python documentation clearly explaining references, including detail about how "an object reference is passed into or out of a function," not to mention the obvious logical inconsistencies in this stance.

Don't be fooled. Python's parameters are object references passed by value, just as is the default in any other OOP language. Those values may be references to mutable objects, but as we've seen above, that really has nothing to do with how the reference was passed. Making up new terms for standard call-by-value behavior only creates confusion where the reality is actually quite simple.

Note that a similar argument sometimes erupts in the Java community; see the excellent article by Scott Stanchfield, "Java is Pass-by-Value, Dammit!"

Immutable Objects

There is only one final wrinkle to consider. We found above that if you have two references to the same object, then a change to the object data made through any of those references will (of course) be visible through all the others. But what if it's not possible to change the object data?

It is easy enough (in most languages) to design a class such that its data is set when the object is created, and can never be changed. If you want to represent different data, you simply create a new object. This is known as an immutable object.

In that case, it makes no sense to talk about modifying the object data through one reference and seeing this change through another — you can't modify it at all. As a result,

Immutable objects act like simple values.

Your variables are still object references in this case, and when you do an assignment or call a function by value, only the object reference is being copied. That's a good thing, if the object contains quite a bit of data that would otherwise be expensive to copy. But because the object is immutable, you avoid any potential confusion that might be caused by one part of the code changing some data that you thought it was only going to inspect.

Most modern languages deal with strings this way: strings are immutable objects, and try as you might, you can't change the characters in a string; you can only create a new string that may contain different characters. This saves a great deal of grief, while maintaining good performance. (Among our example languages, C++ is the exception; its strings are usually mutable.)

In the very first section of this page, we pretended that numeric variables in Python were simple values, as they are in the other languages shown. In fact this is not true; everything in Python is an object, including simple things like integers. However, Python numeric objects are immutable, and so act as if they were values. Thus we can speak loosely and say things like "the value of x is 42", and get away with it, because there is no behavioral difference between this and the true situation ("the value of x is a reference to an immutable object containing 42"). Similar linguistic shorthand can be applied to any immutable type (e.g. strings). Sometimes you'll hear it used for mutable types too, but this should be discouraged (especially when speaking with new programmers), as it is important in that case to remember that each variable contains a reference to the object data, not the object data itself.