Pointers and References

Memory

  • In the context of this tutorial, memory is simply a place/register/space in our RAM where variables are stored.

Pointers

Introduction

  • A pointer is just a Number (integer) that holds memory address of another variable.
  • Size of this number/integer is not fixed. It depends upon OS, CPU architecture etc.
  • For example, 4 bytes for 32 bit system and 8 bytes for 64 bit system.
  • Adding & in front of a vaiable name gives the address in memory where that variable is stored in.
  • In other words, adding & before variable name gives pointer to that variable.
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>

#define PRINT(msg) std::cout << msg << std::endl

int main()
{
bool x;
PRINT(&x);
int y;
PRINT(&y);
double z;
PRINT(&z);
}

Output

1
2
3
0x7fffa8b8fc5b
0x7fffa8b8fc5c
0x7fffa8b8fc60
  • Here, 0x7fffa8b8fc5b is the memory address where variable x is kept at and similar for variables y and z.
  • We can also define a pointer variable to store this address of x:
    1
    2
    3
    void* ptr;
    ptr = &x;
    PRINT(ptr);
    Output
    1
    0x7fffe596ff7c
  • Here, pointer variable ptr is defined as void* ptr.
  • But what does void* term actually mean? Well, the following section explain about this is detail.

Type of Pointer

  • Can a pointer have type?
  • In above example pointers to bool x, int y and double z all looks similar.
  • Indeed, that are same. Value of each of them is 8 byte integer (for 64 bit system).
  • No matter which variable is it pointing, its value is always 8 byte integer.
  • One difference we can notice is, the increment in memory address from x to z is not same.
  • There is increment of 1 between x and y. And increment of 4 between y and z.
  • This is because x is a bool variable which takes 1 bytes of space and y is int variable which takes 4 bytes of space in memory.
  • Basically, &x is the memory address of first byte where x is stored. And, &y is the memory address of first byte where y is stored. Similar for &z.

Note: Variables may not be stored consequiteley in the memory

  • So, the pointer variable which is pointing to the first byte of integer can be referred to as integer pointer. Similar for other variables.
  • void* used in above example is pointer without any specific type.
  • Lets clear this once again. From the perspective of a CPU, all pointers are treated the same. We just assign the different types to pointers such as bool*, int*, float* etc for the ease of use. One advantage is to know which datatype is the given pointer variable pointing to.
  • Another advantage is in Pointer Arthematic. Which is explained in following sections.
  • Type of pointer also comes in handy when we assign specific value to the variable pointed by that pointer.
    1
    2
    3
    4
    5
    int x = 7;
    int* ptr = &x;
    PRINT(*ptr);
    *ptr = 10;
    PRINT(*ptr);
    Output
    1
    2
    7
    10
  • Here, we were able to assign value 10 to int* ptr because we told compiler that ptr holds the variable of type int.:w

Pointer Arthematic

  • We have int* ptr. What might happen if we try to increment it (i.e. ptr++)?
  • Lets Try:
    1
    2
    3
    4
    5
    6
    7
    int x = 1;
    int* ptr = &x;
    PRINT(ptr);
    ptr++;
    PRINT(ptr);
    ptr++;
    PRINT(ptr);
    Output
    1
    2
    3
    0x7ffd7a7be02c
    0x7ffd7a7be030
    0x7ffd7a7be034
  • How about using bool*
    1
    2
    3
    4
    5
    6
    7
    bool x = 1;
    bool* ptr = &x;
    PRINT(ptr);
    ptr++;
    PRINT(ptr);
    ptr++;
    PRINT(ptr);
    Output
    1
    2
    3
    0x7fff91543a1f
    0x7fff91543a20
    0x7fff91543a21
  • Noticed the difference between incrementing int* and bool*?
  • Pointer values are incremented by 4 for int* and by 1 for bool*
  • That is obviously because size of int is 4 bytes and bool is 1 byte.
  • This type of increment was possible because we specified a TYPE to the pointer.

Accessing uninitialized pointers

  • Rule is: Never try to read or write the address pointed by uninitialized pointers
  • Uninitialized pointer variabes will hold random value which points to random section of memory.
  • And if that section is not readable or writable, the program crashes !
1
2
3
int* ptr; // Here ptr is uninitialized
PRINT(ptr); // Lets see what random value it holds
*ptr = 7; // Trying to write to that random adderss crashes the program !

Output

1
2
0x55f2be742140
Segmentation fault (core dumped)

NULL Pointer

  • Can be null (TODO)

More on Pointers

  • Pointer can hold memory address of variable stored in Stack or in Heap. See more about this here.

References

Introduction

  • References are like a disguised version of Pointers.
  • References are like the nickname/alias given to a variable’s Pointer.
  • References themselves are not new variables. They don’t occupy space in the memory. They are just there in our source code to make our work easier.
  • They just “refer” to the existing variable.
    1
    2
    3
    int x = 7;
    int& reference_to_x = x;
    PRINT(reference_to_x);
    Output
    1
    7
  • Here, we have created a reference to the variable x.

Note: & used here has different meaning than that of & used as address of operator !

  • And now we can use this term reference_to_x as if it was x.
  • But what’s fun in that? Why do we even need reference_to_x when we can directly use x?

Pass by Value vs Pointer vs Reference

Pass by Value

1
2
3
4
5
6
7
8
9
10
11
void add_one(int val)
{
val += 1;
}

int main()
{
int x = 7;
add_one(x);
PRINT(x);
}

Output

1
7
  • Here, add_one function copies the int value passed into it and increments the duplicate value.
  • So, x remains unchanged.

Pass by Pointer

1
2
3
4
5
6
7
8
9
10
11
void add_one(int* val)
{
(*val) += 1;
}

int main()
{
int x = 7;
add_one(&x);
PRINT(x);
}

Output

1
8
  • Here, the memory address of x is passed.
  • add_value duplcates the memory address passed into it, but it still points to the same value x
  • Then inreasing *val increases the value in x too.

Pass by Reference

1
2
3
4
5
6
7
8
9
10
11
void add_one(int& val)
{
val += 1;
}

int main()
{
int x = 7;
add_one(x);
PRINT(x);
}

Output

1
8
  • This code looks exactly similar to that in Pass by Value, except one little & after int in add_one function.
  • This behaves exactly as Pass by Pointer but look much pleasing to read and write.
  • It’s just syntatic sugar !!

Criteria of using References (TODO)

  • Must be initialized
  • Cannot be reassigned!