Pointers and References (Part 2: When and How to use them)
Note: This post assumes you know the basics of programming, programming language C/C++ and read Part 1: Declaring and assigning value. This blog is for those who want to learn how variables are stored, how pointers and references work, when and why to use them.
We use a function in our code to make it modular and intuitive. To understand how parameters in function are stored in memory, let's start with how a function call works through the following example.
In memory, there is a call stack to keep track of active/running function calls in a program. When we start our program, stack frame for main is loaded in the call stack.
When execution reaches to function call to 'print' function. Then stack frame for 'print' function is loaded in to call stack as well.
The interesting thing happening here is, 'print' function has it's own variable 'a'. It is stored in a new memory location (we are assuming it to be 0x00005678), has value we passed as parameter ( and same name in this case since we used 'a' for parameter name as well).
The output would be '2010' and variable 'a' in the main function will retain its value. What if we want to update 'a' whenever function like print changes the variable.
We can pass it by reference or we can pass a pointer.
Lets, see what happens if we pass by reference,
In this case, the output will be '2020'. And the value of variable 'a' in the main function would get updated after calling 'print' function.
We can achieve this by passing a pointer to 'a' as well.
Again the output will be '2020'. And value variable 'a' in main will be updated after function call to 'print'.
So, C++ has 2 different (and complex) things, which does same things?
Actually Not.
A pointer is not meant to be used as we did in our last function. Use a pointer when you want to create dynamic data structure or objects, like a linked list, tree. In a linked list you don't have other option than to use a pointer.
For passing a variable, use references. They make code look simple (no '*' in a pass by reference). You don't have to handle cases like a null pointer.
Don't take this as never pass a pointer as an argument. There might be cases when you would want to use both pointer and reference. You might want to pass a pointer by reference. I saw one person asking a question on stack overflow, he was constructing a binary search tree in C++. This was his code for an insert.
Suppose we created an instance of a class tree. Initially, the tree is empty and root is null (which is 0) then we called insert(5) on the tree.
This will call insertNode function passing root which is null at the movement and value 5 as parameters, both by value.
So current in insertNode will have value null and variable value will have a value of 5.
The 'new' call will allocate memory in heap and will update current's value to the memory address. Let's assume it is 0x00001234.
After insertNode execution is done we will lose current.
In order to make this work, what it should have been
This might look complicated and may seem unnecessary (in this case mostly it was). But in the next part, we will explore how we might have done this multiple times without realizing in other languages.
We use a function in our code to make it modular and intuitive. To understand how parameters in function are stored in memory, let's start with how a function call works through the following example.
void print(int a)
{
cout << a;
}
int main()
{
int a = 10;
print(a);
return 0;
}
In memory, there is a call stack to keep track of active/running function calls in a program. When we start our program, stack frame for main is loaded in the call stack.
When execution reaches to function call to 'print' function. Then stack frame for 'print' function is loaded in to call stack as well.
The interesting thing happening here is, 'print' function has it's own variable 'a'. It is stored in a new memory location (we are assuming it to be 0x00005678), has value we passed as parameter ( and same name in this case since we used 'a' for parameter name as well).
void print(int a)
{
a = 20;
cout << a;
}
int main()
{
int a = 10;
print(a);
cout << a;
return 0;
}
The output would be '2010' and variable 'a' in the main function will retain its value. What if we want to update 'a' whenever function like print changes the variable.
We can pass it by reference or we can pass a pointer.
Lets, see what happens if we pass by reference,
void print(int &a)
{
a = 20;
cout << a;
}
int main()
{
int a = 10;
print(a);
cout << a;
return 0;
}
In this case, the output will be '2020'. And the value of variable 'a' in the main function would get updated after calling 'print' function.
We can achieve this by passing a pointer to 'a' as well.
void print(int *a)
{
*a = 20;
cout << *a;
}
int main()
{
int a = 10;
print(&a);
cout << a;
return 0;
}
Again the output will be '2020'. And value variable 'a' in main will be updated after function call to 'print'.
So, C++ has 2 different (and complex) things, which does same things?
Actually Not.
A pointer is not meant to be used as we did in our last function. Use a pointer when you want to create dynamic data structure or objects, like a linked list, tree. In a linked list you don't have other option than to use a pointer.
For passing a variable, use references. They make code look simple (no '*' in a pass by reference). You don't have to handle cases like a null pointer.
Don't take this as never pass a pointer as an argument. There might be cases when you would want to use both pointer and reference. You might want to pass a pointer by reference. I saw one person asking a question on stack overflow, he was constructing a binary search tree in C++. This was his code for an insert.
Node* root = NULL;
void insertNode(Node* current, int value)
{
if(current == NULL)
{
current= new Node(value);
}
else if (val < current->value)
{
insertNode(current->left, value);
}
else
{
insertNode(current->right, value);
}
}
insert(int value)
{
insertNode(root, value);
}
Looks like the code should have worked as it is meant to. But surprisingly (to him and many others), it didn't work. The problem here is, he is passing a pointer to a node (a variable of type Node*) by value.Suppose we created an instance of a class tree. Initially, the tree is empty and root is null (which is 0) then we called insert(5) on the tree.
This will call insertNode function passing root which is null at the movement and value 5 as parameters, both by value.
So current in insertNode will have value null and variable value will have a value of 5.
The 'new' call will allocate memory in heap and will update current's value to the memory address. Let's assume it is 0x00001234.
After insertNode execution is done we will lose current.
In order to make this work, what it should have been
Node* root = NULL;
void insertNode( Node* ¤t, int value)
{
if(current == NULL)
{
current= new Node(value);
}
else if (val < current->value)
{
insertNode(current->left, value);
}
else
{
insertNode(current->right, value);
}
}
insert(int value)
{
insertNode(root, value);
}
Noticed, the only thing we changed is we are passing node pointer as a reference.This might look complicated and may seem unnecessary (in this case mostly it was). But in the next part, we will explore how we might have done this multiple times without realizing in other languages.








Comments
Post a Comment