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);
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
voidadd_one(int val) { val += 1; }
intmain() { 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
voidadd_one(int* val) { (*val) += 1; }
intmain() { 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
voidadd_one(int& val) { val += 1; }
intmain() { 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.