Reference and Pointers in C++

 

by - Yash Ukalkar | 



Introduction - 

Pointers and reference variables are often used when one needs to write programs that control and manage the memory available in the machine. Pointers work close to memory and allow us to implement data storage and processing in an efficient manner. For example, a linked list is a data structure that uses pointers and allows us to store and delete data, in a sequential manner, but faster than many other methods.


Reference Variables - 

Simply put, a variable is a name given to a block of the machine's memory where some value is stored. A reference variable is another name given to a pre-existing variable, i.e. a new name that is given to the memory block of the variable to which the reference variable refers to.

Syntax:

ref_var_datatypereference_variable = referred_variable;

here,

  • ref_var_datatype is the data type of the reference variable
  • reference_variable is the name of the reference variable created
  • referred_variable is the variable to whom we want the reference_variable to refer to
The ref_var_datatype is the same as that of the referred_variable.

Now, one can use the reference_variable instead of the referred_variable to access the same memory location. This may come handy to shorten long variable names, temporarily, to facilitate faster code writing but is not advised.

Ex:

#include<iostream>
using namespace std;   
int main(){
string tesla_model_X = "Best electric car!"; stringtmx = tesla_model_X;
cout<<"Value from original variable-\n"<<tesla_model_X;
cout<<"\n\n";
cout<<"Value from reference variable-\n"<<tmx;
return 0;
}

Output:







Referencing - 

Before we move on to pointers, we need to understand referencing and the use of the reference operator(&).

The reference operator & is used before a variable name(pre-declared) to obtain the address of the memory reserved by that variable. So, referencing means accessing memory addresses of variables for further processes.

Ex:

#include<iostream> using namespace std;
int main(){
string car = "Benz";
cout<<"Variable value = "<< car;
cout<<endl;
cout<<"Variable address = "<< &car;
return 0;
}

Output:




here, we can see that the value of the variable car is stored at some memory on the computer whose location is 0x7bfde0, accessed using the reference operator.

Other than it's use for pointers, referencing does help use make better, efficient programs(ex: used in passing references as arguments in functions) by letting us work closer to the hardware of a machine.



Pointers - 

Pointers are special types of variables which store memory addresses, which are hexadecimal codes, as their value, exclusively. These can be used to refer to a specific block of memory on a machine rather than a value of some type. Since they also are variables, pointers too occupy some space on the machine, and the space provided to store memory addresses depends on the compiler used.

Syntax:

ptr_datatypeptr_name = &variable;

here, 

  • ptr_datatype is the data type of the pointer variable and must be the same as that of the variable to whose memory address this pointer stores.
  • ptr_name is the name given to the pointer which can be used to access it's contents.
  • variable is the variable to which the pointer ptr_name points to(i.e. stores the memory address of variable obtained using & operator).

The asterisk(*) is used to tell the compiler that this variable is a pointer and can only store memory addresses. The declaration/definition of pointers can involve the positioning of * in following 3 ways, all of which are correct:

ptr_datatype* ptr_name = &variable; //preferred way
ptr_datatype *ptr_name = &variable;
ptr_datatype * ptr_name = &variable;


Ex:

#include<iostream>
using namespace std;
int main(){
int number = 10;
intnumber_pointer = &number;
cout<<"Variable's value = "<<number<<endl;
cout<<"Variable's address = "<<&number<<endl;
cout<<"Pointer value = "<<number_pointer;
return 0;
}

Output:





Dereferencing:

Dereferencing a pointer means to access the 'value' of the variable to which it points. If used by it's name, a pointer will give us only the hexadecimal address it stores. But, if we use an asterisk * symbol before the pointer name, it gives us the value that is stored at the memory address that the pointer contains.

Ex:

#include<iostream>
using namespace std;
int main(){
    int number = 10;
    intnumber_pointer = &number;
    cout<<"Pointer value = "<< number_pointer <<endl;
    cout<<"Variable's value = "<< *number_pointer;
    return 0;
}

Output:




Changing values using pointers:

Now that we know the value of the variable a pointer points to can be accessed by dereferencing the pointer, another thing to note is that we can also change the value of the variable that the pointer points to simply by changing the value of dereferenced pointer.

Ex:

#include<iostream>
using namespace std;
int main(){
    float pi = 3.14159;
    floatpi_ptr = &pi;
    cout<<"Pi value = "<<pi<<endl;
    //Changing value after dereferencing pointer
    *pi_ptr = 3.14
    cout<<"Pi value = "<<pi<<endl;
    return 0;
}

Output:





Incrementing/Decrementing pointers:

We know that we can increase the value of a variable by 1 using the increment(++)/decrement(--) operators. This however, works a little different with pointers because unlike variables that store numbers/characters, pointers store memory addresses as hexadecimal values.

We must understand that the memory blocks available in our computers are consecutive and as some might have predicted, incrementing/decrementing the value of a pointer can make it point to a memory address after/before(respectively) the memory block to which it currently points to.

Let's consider the following example of code, and scroll down slowly as to not see the output before you make a guess.

#include<iostream> using namespace std;
int main(){
int num = 5;
intnum_ptr = &num;
cout<<"Pointer value = "<<num_ptr<<endl;
num_ptr++;
cout<<"Pointer value = "<<num_ptr<<endl;
return 0;
}

here, what do you think will be the value of num_ptr after it is incremented in the 8th line?

Ok, to complete the question, the initial value of num_ptr is 0x7bfe14.

Output:




If you guessed that the new value will be 0x7bfe15, then I'm sorry because you guessed it wrong. Let's understand why that was not the case.

The value of num_ptr increases not by 1 but by 4 and the reason was the data type of the pointer, which is why the data type of the pointer needed to be the same as that of the variable it pointed to. 

We know that every data type has, by default, a specific storage space. For example, an int variable has 4 bytes of memory reserved to store integer numbers, char variable has 1 byte of memory reserved to store a character, a double variable requires 8 bytes of memory reserved to store a floating point number.

Before the increment operator is used, the memory locations 0x7bfe14, 0x7bfe15, 0x7bfe16, 0x7bfe17, are reserved to store the value that is inside the num variable and hence, the next unoccupied/unreserved memory location, which is 0x7bfe18, is used after the num_ptr is incremented. If decrement operator was used instead, the memory location given would be 0x7bfe10.

The same follows for a pointer of any data type, and the next unoccupied/unreserved memory location is provided after incrementing/decrementing the pointer variable.

Ex:

#include<iostream> using namespace std;
int main(){
double pi = 3.14159;
doublepi_ptr = &pi;
cout<<"Pointer value = "<<pi_ptr<<endl;
pi_ptr--;
cout<<"Pointer value = "<<pi_ptr<<endl;
 pi_ptr--;
cout<<"Pointer value = "<<pi_ptr<<endl;
pi_ptr--;
cout<<"Pointer value = "<<pi_ptr<<endl;
return 0;
}

Output:






Helpful tip - 

1) Since pointers access memory locations and in C++, a garbage value is fed to the variables that are declared but not initialized, it is not good if the pointer accesses invalid memory locations, or at worse crashes by doing so. So, one must make a non instantiated, declared pointer a null pointer to avoid such problems.

Ex:

#include<iostream>
using namespace std;
int main(){
    double
null_pointer = NULL;
    
cout<<null_pointer;
    
return 0;
}

Output:



2) Since pointers are used to access and manipulate computer memory, it is very important to make sure that no unintended changes are made to the values of other variables while manipulating pointers. So, we use the const keyword in three different ways to avoid such things-

  • To avoid changes in the memory address(value stored can be changed) 

ptr_datatypeconst ptr_name = &variable;

In this case, we cannot reassign another variable to the created pointer, it will point to the same variable location once instantiated. It's value however can be changed.

  • To avoid changes to the value at the stored memory location(another location can be assigned to the pointer)

const ptr_datatypeptr_name = &variable;

In this case, we can reassign another variable to the pointer but the value at the pointed location cannot change at any given time.

  • To avoid changes to both, the location stored and the value at that location

const ptr_datatypeconst ptr_name = &variable;

In this case, neither the pointer can be reassigned to another variable location, nor can the value stored at the instantiated location be changed.


You now know a little about pointers in C++ and I assure you that pointers are very useful as you will see ahead while learning about dynamic memory allocation(creating more space for variables during run-time), various data structures and many more things. Keep learning till then.

THANK YOU!

Comments

Popular posts from this blog

What Is Functions In C++ ? || Explained By Examples || Function Types

Basics Of Operators||Basics of operators in C++||Basics of operators in cpp||Types of operators in C++||Arithmetic Operators||Comparision Operators||Binary Operators||Logical Operators||Bitwise Operators||Assignment Operators||Miscellaneous Operators||Bitwise Explained||Operators Precedence||