Understanding Pointers & References in C++

Pointers & References in C++
Pointers & References in C++

Introduction

C++ is one of the most widely accepted programming languages and it has found its way in hardware systems and OS. When it comes to C++ then pointer and references are one of the basic building blocks which give the programmer power to use one variable and provide access to another.

Now sometimes it happens that there is confusion between pointers and references because they look quite similar in function. But that’s not the case. So, let’s start with understanding in detail what pointers and references are, and then we can compare them to get a clear idea about them.

Pointers

Consider a pointer as a variable that is given a task to hold the memory address of another variable. Also if we want to access that particular memory location then we have to use * operator preceding pointer variable.

How to initialise a pointer:
Case 1:
int a = 100;     // Consider an integer variable a storing value 100
int *b = &a;     // Here the variable b is a pointer which is storing memory address
Case 2:
int *b;            // Initialisation of pointer variable b
b = &a;         // Assigning address of variable a to pointer b
Note : (ampersand represents that address is being accessed)
How to reassign existing pointer:
int a = 25;     // Integer variable assigned with value 25
int b = 30;     // Integer variable b assigned with value 30
int *x;            // Declaring pointer x
x = &a;         // Assigning value of variable a to pointer x via accessing memory address 
x = &b;         // Assigning value of variable b to pointer x via accessing memory address

Application of Pointers:

  • It is used to implement a variety kinds of data structures.
  • They are also used to store and manage the addresses of dynamically allocated blockers of memory.
  • The best application for pointers is Two-Point techniques.

Example:

#include <iostream>
using namespace std;
int main(){
//Pointer declaration
int p, var=101; //Assignment p = &var; cout<<"Address of var: "<<&var<<endl; cout<<"Address of var: "<<p<<endl; cout<<"Address of p: "<<&p<<endl; cout<<"Value of var: "<<p;
return 0;
}

References

Now the reference variable can be considered as an alias for an existing variable. So, the reference is like a pointer whose address remains constant and the compiler will apply the * operator.

How to initialise a reference:
int a = 30;     // Integer variable a assign value 30
int &x = a;     // Here we are providing reference of a to x

Example:

#include <iostream>
using namespace std;
int main () {
// declare simple variables
int i;
double d;
// declare reference variables
int& r = i;
double& s = d;
i = 5;
cout << "Value of i : " << i << endl;
cout << "Value of i reference : " << r << endl;
d = 11.7;
cout << "Value of d : " << d << endl;
cout << "Value of d reference : " << s << endl;
return 0;
}

Note: 

  • One needs to take into consideration that we should declare and initialize references at a single step.
  • Also, the references cannot be re-assigned and must be assigned at initialization.

Now, what is the application of the references?

It is used in function parameters and returns types.

Key facts to remember about Pointers and References:

  • A pointer has its own memory address and size on the stack but when considering the case scenario of references they share the same memory address but they do take size on the stack.
  • Another thing to keep in mind is that a pointer can be assigned null but that can not be a case in reference.
  • Also in case of a pointer, you can have multiple levels of indirection, like a pointer pointing towards b and b on c and so on. But in the case of references, there is only one level of indirection.

Why does C++ have both pointers and references?

C++ inherited pointers from C, so they couldn’t be removed without causing serious compatibility problems. References are useful for several things, but the direct reason they were introduced in C++ was to support operator overloading

Example:

void f1(const complex* x, const complex* y) // without references
{
complex z = x+y; // ugly
// …
}
void f2(const complex& x, const complex& y) // with references
{
complex z = x+y; // better
// …
}

More generally, if you want to have both the functionality of pointers and the functionality of references, you need either two different types (as in C++) or two different sets of operations on a single type. For example, with a single type, you need both an operation to assign to the object referred to and an operation to assign to the reference/pointer. This can be done using separate operators (as in Simula).

Example:

Ref<My_type> r :- new My_type;
r := 7; // assign to object
r :- new My_type; // assign to reference
Alternatively, you could rely on type checking (overloading).
Example:
Ref<My_type> r = new My_type;
r = 7; // assign to object
r = new My_type; // assign to reference

When should I use references and pointers?

Use references when you can, and pointers when you have to.

References are usually preferred over pointers whenever you don’t need “reseating”. This usually means that references are most useful in the class’s public interface. References typically appear on the skin of an object, and pointers on the inside. The exception to the above is where a function’s parameter or return value needs a “sentinel” reference — a reference that does not refer to an object.

This is usually best done by returning/taking a pointer and giving the nullptr value this special significance (references must always alias objects, not a dereferenced null pointer).

Reference Vs Pointers:

Pointers and references are equivalent, except the following:

  • A reference is a name constant for an address. You need to initialise the reference during declaration. int & iRef; // Error: ‘iRef’ declared as reference but not initialised.
  • Once a reference is established to a variable, you cannot change the reference to reference another variable.
  • To get the value pointed to by a pointer, you need to use the dereferencing operator  (e.g., if pNumber is a int pointer, pNumber returns the value pointed to by pNumber. It is called dereferencing or indirection). To assign an address of a variable into a pointer, you need to use the address-of operator & (e.g., pNumber = &number).
  • On the other hand, referencing and dereferencing are done on the references implicitly. For example, if refNumber is a reference (alias) to another int variable, refNumber returns the value of the variable. No explicit dereferencing operator * should be used. Furthermore, to assign an address of a variable to a reference variable, no address-of operator & is needed.

Example:
/* References vs. Pointers (TestReferenceVsPointer.cpp) */

include <iostream>
using namespace std;
int main() {
int number1 = 88, number2 = 22;
// Create a pointer pointing to number1
int * pNumber1 = &number1; // Explicit referencing
*pNumber1 = 99; // Explicit dereferencing
cout << *pNumber1 << endl; // 99
cout << &number1 << endl; // 0x22ff18
cout << pNumber1 << endl; // 0x22ff18 (content of the pointer variable -
same as above)
cout << &pNumber1 << endl; // 0x22ff10 (address of the pointer variable)
pNumber1 = &number2; // Pointer can be reassigned to store another
address

// Create a reference (alias) to number1


int & refNumber1 = number1; // Implicit referencing (NOT &number1)
refNumber1 = 11; // Implicit dereferencing (NOT refNumber1) cout << refNumber1 << endl; // 11 cout << &number1 << endl; // 0x22ff18 cout << &refNumber1 << endl; // 0x22ff18 //refNumber1 = &number2; // Error! Reference cannot be re-assigned // error: invalid conversion from 'int' to 'int'
refNumber1 = number2; // refNumber1 is still an alias to number1.
// Assign value of number2 (22) to refNumber1 (and
number1).
number2++;
cout << refNumber1 << endl; // 22
cout << number1 << endl; // 22
cout << number2 << endl; // 23
}

A reference variable provides a new name to an existing variable. It is dereferenced implicitly and does not need the dereferencing operator to retrieve the value referenced. On the other hand, a pointer variable stores an address. You can change the address value stored in a pointer. To retrieve the value pointed to by a pointer, you need to use the indirection operator, which is known as explicit dereferencing. Reference can be treated as a const pointer. It has to be initialized during declaration, and its content cannot be changed.

Reference is closely related to the pointer. In many cases, it can be used as an alternative to the pointer. A reference allows you to manipulate an object using a pointer, but without the pointer syntax of referencing and dereferencing. The above example illustrates how reference works but does not show its typical usage, which is used as the function formal parameter for pass-by-reference.

Conclusion

Pointers and references are highly complex and difficult to master. But they can greatly improve the efficiency of the programs. For novices, avoid using pointers in your program. Improper usage can lead to serious logical bugs. However, you need to understand the syntax of pass-by-reference with pointers and references, because they are used in many library functions.

  • In pass-by-value, a clone is made and passed into the function. The caller’s copy cannot be modified.
  • In pass-by-reference, a pointer is passed into the function. The caller’s copy could be modified inside the function.
  • In pass-by-reference with reference arguments, you use the variable name as the argument.
  • In pass-by-reference with pointer arguments, you need to use &varName (an address) as the argument.

Once a reference is created, it cannot be later made to reference another object; it cannot be resented. This is often done with pointers.

References cannot be NULL. Pointers are often made NULL to indicate that they are not pointing to any valid thing. A reference must be initialized when declared. There is no such restriction with pointers and understand more about the size of the pointers.

To read more about C++, click here.

By Akhil Sharma