Pointer and Reference Variables

C++ supports two schemes for having one identifier be an alias for some instance of a data element: pointers and references. Suppose I have: "double x = 3.8;". I can create a reference variable, r and/or a pointer variable, p, both of which can be used to access and/or modify the value in x. This page begins by giving you some insight into how the two schemes are similar and how they are different. It then focuses primarily on pointers since they are (i) so critical in terms of dynamic memory management, and (ii) the source of so many horrendous programming bugs!


When declaring variables, C++ reference variables are declared by prepending an ampersand (&) onto the variable name being declared; C++ pointer variables are declared by prepending an asterisk (*) to the name. For example:

Employee &fred  // "fred" is a reference variable
Employee *sally // "sally" is a pointer variable

Unlike pointers, reference variables must be bound to a specific object at the time of declaration. Consider the following local section of code:

Employee chris(…);          // A stack-allocated instance of Employee called "chris"
Employee* empPtr1;          // OK (but dangerous, as we will see below)
Employee* empPtr2 = &chris; // OK (Note "&" here means "take address of")
Employee& empRef1;          // Compilation error!
Employee& empRef2 = chris;  // OK
…

Recall we first encountered reference variables when talking about formal parameters in the Parameter passing section. Formal parameters that are reference variables are bound at the time of instantiation. For example, consider the following prototype:

void examineEmployee(Employee& emp)

When examineEmployee is called, the net effect from the perspective of the formal reference parameter is the same as if we had written:

Employee& emp = actual_parameter;

Once bound, a reference variable can never be made to refer to a different object. For example, after declaring empRef2 as above, consider:

Employee pat(…);
…
empRef2 = pat; // COPIES the data associated with pat, OVERWRITING the data associated with chris!

Pointer variables, on the other hand, can be changed at any time to point to a different object.

empPtr2 = &pat; // now empPtr2 points to pat, but no data copying of any sort has occurred

Note that Java object references behave more like C++ pointer variables than C++ reference variables.

A cautionary note: C++ syntax is insensitive to white space around either the ampersand or the asterisk. For example, all three of the following declare that x is a pointer to an integer:

int * x;
int* x;
int *x;

Individual coding preferences prevail, but be sure to remember that the ampersand or asterisk is interpreted as applying only to the immediately following variable. Hence:

double* v1, v2;

declares v1 to be a pointer to a double and v2 to be an actual double (not a pointer). If we wanted both to be pointers to doubles, we could write:

double * v1, * v2;

Now let's focus strictly on pointers

What the pointer declaration does (and what it does not do)

For any data type (e.g., SomeType), declaring:

SomeType* onePtr;

creates a variable called "onePtr" that can point to an instance of "SomeType". However:

Therefore it is good programming practice to always declare and initialize pointer variables at the same time.

A special pointer that does not point to anything

Sometimes we don't wish to initialize our pointer to an existing object, but we want to be able to determine later that it doesn't point to anything. In current versions of C++ (C++ 11 or later), the reserved word nullptr is used:

SomeType* onePtr = nullptr;

(Depending on the version of the g++ (or other) compiler that you use, you may need to specify the compiler flag -std=c++11.)

Older versions of C++ used a standard preprocessor-defined constant NULL:

SomeType* onePtr = NULL;

The newer nullptr is preferred for several reasons. The older NULL also continues to be available.

The Most Common Ways to Declare and Initialize Pointer Variables

Assume that Foo has been defined as a class and that any appropriate constructors have been defined. (In particular, note that items 2, 4, and 5 assume that a no-parameter constructor for class Foo exists.)

  1. // Declare and initialize to point at nothing:
    int* p1 = nullptr;
    Foo* fooPtr = nullptr;
  2. // Declare and initialize to point to an existing variable of the same type:
    // WARNING: Potentially Dangerous – Note that the text
    // explicitly states that it never uses this facility.
    int x = 27;
    int* potentiallyDangerousIntPtr = &x;
    Foo localFoo;
    Foo* potentiallyDangerousFooPtr = &localFoo;
  3. // Declare and initialize to point to the same variable that another pointer points to:
    int* p3 = p1;
    Foo* anotherFooPtr = fooPtr;
  4. // Declare and initialize to a dynamically allocated instance of the variable:
    int* p4 = new int;
    // BUT CONTENTS of *p4 are uninitialized, so:
    *p4 = 17; // See "Dereferencing a Pointer" below

    // For class Foo:
    Foo* dynFooPtr = new Foo;
    // The contents of *dynFooPtr WILL be initialized since the Foo constructor gets called.
    // Nevertheless, we can still overwrite the data. For example:
    *dynFooPtr = localFoo;
  5. // Create a dynamically allocated array
    int size = some-integer-expression;
    int* arr = new int[size];
    // However, none of the array elements are initialized. Hence, for example:
    for (int i=0 ; i<size ; i++)
        arr[i] = 3*i + 2;

    Foo* fooArray = new Foo[size];
    // Each element of fooArray is initialized since its constructor will have been called. But
    // if we choose, we can also overwrite that data. For example:
    for (int i=0 ; i<size ; i++)
        fooArray[i] = localFoo;

Dereferencing a Pointer

Dereferencing means following a pointer and accessing the data to which it points. There are three ways to dereference a pointer, ptr:

All three of these forms can be used on either the left-hand or the right-hand side of an assignment statement.

Note that *ptr is equivalent to ptr[0].

Any attempt to dereference a pointer currently set to nullptr (or NULL) will cause your program to immediately crash.

Pointer: Instance or Array?

Consider items 4 and 5 above (and the later comment: "Note that *ptr is equivalent to ptr[0]."). How can I determine whether a pointer refers to a single instance (as does "dynFooPtr") or to a conventional C++ array (as does "fooArray")? Note that both are declared simply as "Foo*".

The answer is: you can't!

There is nothing in the language specification that allows the runtime system to determine whether a variable declared as T* for some type, T points to a single instance or to a conventional C++ array of instances. For example, both of the following are perfectly legal, and it is up to the programmer to make sure that an appropriate actual parameter is supplied when either function is called:

void doSomething(double* quibbet)
{
    *quibbet = 3.7;
}

void doSomethingElse(double* quibbet)
{
    for (int i=0 ; i<3 ; i++)
        quibbet[i] = 0.0;
}

A programming practice I use to try to make things clearer is to specify the function prototype for functions like the second as:

void doSomethingElse(double quibbet[]); // and probably want to pass the length of "quibbet"

But note that this is a programming documentation convention only – the compiler considers formal parameters declared as "T* blah" to be absolutely identical to formal parameters declared as "T blah[]"! Hence the additional complementary practice I follow is that if my intent is really to pass just a single instance of an actual parameter to a method/function inside of which the parameter is to be modified, I pass it by reference instead of by address. Hence I would write:

void doSomething(double& quibbet)
{
	quibbet = 3.7;
}

Not only is this clearer, but also it is impossible to treat quibbet as an array when passed this way.

The Three Roles of * and &

Binary OperatorUnary Operator in an Expression Unary Declaration Modifier
*MultiplyDereference "what follows can hold a pointer to"
&Bitwise ANDReturn the address of "what follows will hold a reference to"