Pointers as Function Arguments or call by reference in C

In this tutorial about pointers in C/C++ programs, we will learn how to use pointers as function arguments or function call by reference in C/C++. This is one of the use cases for pointer variables also known as call by reference.

Pointers as Function Arguments

Before diving straight into the topic first let us look at an example C program involving a user defined function. The aim of this program was to decrement the integer variable ‘x’ using the user defined function void decrement().

#include <stdio.h>
void decrement(int x)
{
x = x - 1;
}
int main(){
int x;
x=100;
decrement(x);
printf("x = %d", x);
}

How the Code Works?

As you can see the integer variable ‘x’ is declared and initialized to 100 in the main() method. The purpose was to decrement the value of the variable ‘x’ by one. Instead of specifying x =x -1 in the main(), the decrement() function was used instead.

int main(){
int x;
x=100;
decrement(x);
printf("x = %d", x);
}

decrement() is a user defined function that takes in the integer variable ‘x’ as an argument and decrements its value by 1.

void decrement(int x)
{
x = x - 1;
}

This function is called inside the main() with ‘x’ as the argument. Then, the value of ‘x’ is printed as an output.

printf("x = %d", x);

The person who wrote this piece of sketch expects that the value of ‘x’ will be decremented by 1 and will thus be (100-1) 99. Let us see if this is the case or not.

Code Output

Now let’s see the code output. After the compilation of the above code, you will get this output.

Pointers to function arguments output demo1

As you can see the value of x is still 100 which was the value it was initialized with. It did not decrement. Let us see why this happened and how to resolve it?

Problem with the Code

The issue with the above program code is that the integer variable ‘x’ is not being accessed properly. Whenever we declare a variable inside a function it is called a local variable. By using the variable name we can access that variable only in the same function in which we have declared it.

Thus, the variable ‘x’ in the function decrement() and the variable ‘x’ in the main() are not the same variables!

When main() calls the decrement() method and passes the ‘x’ as an argument to the function then only the value of x is copied to this another x which is another variable local to the decrement() function.

Let us modify this code to show you what is happening by adding two print statements. One print statement is added in the decrement() function to print the address of the variable ‘x.’ Likewise, another print statement is added in the main() to print the address of the variable ‘x’ present in the main(). Remember to access the address of a variable, we add ‘&’ in front of the variable.

#include <stdio.h>
void decrement(int x)
{
x = x - 1;
printf("Address of 'x' in decrement() = %d\n", &x);
}
int main(){
int x;
x=100;
decrement(x);
printf("Address of 'x' in main() = %d\n", &x);
//printf("x = %d", x);
}

Code Output

Now let’s see the code output. After the compilation of the above code, you will get this output.

Pointers to function arguments output Demo 2

Notice that the addresses are different for the variable ‘x’ in decrement() and main() showing that it is indeed two variables and not the same. If the variable ‘x’ in decrement() and main() was same then the addresses would have been the same as well.

Application Memory

To understand this better, let us see what happens in a computer’s memory when a program executes. When an application is started, the computer sets aside some amount of memory for the execution of the program. This memory is divided into four categories as shown in the diagram below.

Pointers to function arguments Application memory
Application Memory

One part of memory is allocated to store the various instructions in the program (code). The computer needs to keep all its instructions in the memory e.g. sequential instructions. Another part of the allocated memory is for the global variables. If we do not declare a variable inside a function in C/C++ then it is known as a global variable. Global variables can be accessed and modified anywhere in the program unlike local variables which can be accessed and modified within a particular function/ code block.

The third part of the memory is called stack. This is a very important segment for this guide. This is where all the local variables are stored. The fourth part is the heap which we will discuss in later guides. Of these four sections of the allocated memory: the code segment, the global variables and the stack are fixed. They are decided when a program starts executing. The application can however ask for more memory for heap section during execution.

RAM

Let us show you the computer RAM when a program executes. Each byte in the memory is addressable. Lets say the memory allocated for our program is from 200-900 with various segments of our application’s memory. Now for e.g. address 400-700 is allocated for STACK. From 400-700 this part of the memory is assigned for our program as shown below:

Pointers to function arguments RAM pic1
RAM

For example purposes, we will use the program code that we specified in the first section.

#include <stdio.h>
void decrement(int x)
{
x = x - 1;
}
int main(){
int x;
x=100;
decrement(x);
printf("x = %d", x);
}

When the program starts the main() method is initially invoked. All the information about the method call for example parameters, local variables, the calling function to which it should return, the current instruction at which it is executing basically all this information is stored in the STACK.

Stack Frame

We will take out from the stack some part for the main() method and create a unit called stack frame. Each function will have stack frame associated with it. Inside this stack frame we have our variable ‘x.’ Memory is allocated for x from this stack frame and its value is set to 100.

Pointers to function arguments RAM pic2

Calling decrement() in main()

Additionally, the main() method also calls decrement() function. What happens then? The machine stops the execution for some time. It goes ahead and finishes the decrement() method first and then it resumes back to the main() method. Thus, another stack frame is allocated, this time for the decrement() function with the parameters of the decrement() function like ‘x’. New local variables are created corresponding to these parameters. Whatever values have been passed are copied to these variables as well.

Pointers to function arguments RAM pic3

Now inside the decrement() function the following statement x =x-1 is executed. What happens is that this ‘x’ which is local to this particular decrement() function, in this particular stack frame, this ‘x’ is decremented by 1 as shown below. Note that we can not access this variable outside its stack frame.

Pointers to function arguments RAM pic4

When decrement() finishes the control returns to the main() method. The machine clears the stack frame that was allocated for decrement(). Main() method resumes again which was paused before. Thus, the life time of a local variable is only till the time the function is executing.

Pointers to function arguments RAM pic2

Now, the next statement in the main() function is printf(). The state of execution of main method is paused and printf executes. This particular structure is known as function call stack. Whichever function is at the top of this stack executes.

Pointers to function arguments RAM pic5

Remember the stack is fixed in size so if you have a scenario where one function keeps calling another function indefinitely then the memory of the stack will overflow. Hence, the program will crash.

Call by Value

Let us look at our decrement() function. Here ‘x’ is in the stack frame of the main() method.

void decrement(int x)
{
x = x - 1;
}

The main() is our calling function and decrement() is our called function. When we call a function in the calling function, the argument is also known as ‘actual argument.’ In the called function, the argument is known as ‘formal argument.’ All that happens is that the actual argument is actually mapped to a formal argument. So when this function call happens, ‘x’ as an actual argument is mapped to another ‘x’ which is a formal argument.

Instead of ‘x’ if had a ‘y’ here, then we would have written the decrement function as shown below:

void decrement(int y)
{
y = y - 1;
}

In this case ‘x’ would have mapped to ‘y’. So the value of ‘x’ will be copied to the variable ‘y.’

Now if we make such a function call, we basically have a variable we map to another variable, the value in one variable copied to another variable then such a function call is called CALL BY VALUE. This was what was happening in the initial sketch. A call by value was being made that is why the desired result was not obtained. Now how to get the desired result? Let us look into that.

Call by Reference

We want to use the variable ‘x’ local to the main() method inside the decrement() function. This will be done if we use pointers as function arguments. We have modified the initial code below to give the desired outcome. Let us look into it.

#include <stdio.h>
void decrement(int *ptr)
{
*ptr = *ptr - 1;
}
int main(){
int x;
x=100;
decrement(&x);
printf("x = %d", x);
}

Notice that the decrement() function’s argument is not an integer variable anymore. In fact the argument is a pointer to an integer. As you already known, pointer to integer is used to store the address of the integer. In the decrement() function, we ware passing the address of ‘x’ as &x. When the program will execute, the main() method will be invoked first.

For example let us say that the stack shown below corresponds to this program sketch. The stack frame of the main() method has address from 400-500. Now there will be a local variable ‘x’ in this main() method. The address at which ‘x’ is stored is 450 and the value stored in it is 100.

Pointers to function arguments stack pic1

When main() method calls decrement(), then a local variable corresponding to the parameter ‘ptr’ is created. This is a pointer to integer and the value that is passed to this variable will be 450 (the address of x). Thus, ptr is pointing to x.

Pointers to function arguments stack pic2

Now when we say *ptr we are dereferencing this address. The following statement in the decrement() function : *ptr = * ptr – 1 means that *ptr is the value stored at the address ptr decremented by 1. Hence, (100-1=99) So ‘x’ is now 99.

Pointers to function arguments stack pic3

When decrement() finishes and we come back to the main() method. The print statement gets executed. As x is now 99 thus this is the output that will be printed. Now let’s see the code output. After the compilation of the above code, you will get this output.

Pointers to function arguments output Demo 3

Dynamic Memory Allocation through Double Pointer and Function

#include <stdio.h>
#include <stdlib.h>

void allocate(int **a){
    *a = (int*)malloc(sizeof(int)*10);
}
int main(){
    int *p = NULL;
    allocate(&p);
    for(int i=0; i<=9; i++)
    *(p+i) = i;
    for(int i=0; i<=9; i++)
    printf("%d\n", *(p+i));
    return 0;
}

Points to Note

A function call in which instead of passing the value of a variable we pass the address of the variable so that we have a reference to the variable and we can dereference it and perform some operations is called call by reference. So if we use pointers as functions arguments then we are using call by reference. Call by reference can save us a lot of memory because instead of creating a copy of a large and complex data type we just use the reference to it. Using a reference will make our program code efficient and lead to less memory consumption.

You may also like to read:

Leave a Comment