Pointers are arguably the most difficult feature of C to understand. But, it is one of the features which make C an excellent language. This is the third article in the series. You can read the second one here.
Topics -
0. Call by Value v/s Call by Reference
1. Pointers as Function Arguments
2. Pointers as Function Return
3. Pointer to Function
4. Array Of Pointers to Functions
5. Pointer to Function as an Argument
0. Call by Value v/s Call by Reference
Have a look at the program below.
#include <stdio.h>
int multiply(int x, int y){
int z;
z = x * y;
return z;
}
main(){
int x = 3, y = 5;
int product = multiply(x,y);
printf("Product = %d\n", product);
/* prints "Product = 15" */
}
The function multiply()
takes two int
arguments and returns their product as int
. In the function call multiply(x,y)
, we passed the value of x
and y
( of main()
), which are actual arguments, to multiply()
.
The values of the actual arguments are passed or copied to the formal arguments x
and y
( of multiply()
). The x
and y
of multiply()
are different from those of main()
. This can be verified by printing their addresses.
#include <stdio.h>
int multiply(int x, int y){
printf("Address of x in multiply() = %d\n", &x);
printf("Address of y in multiply() = %d\n", &y);
int z;
z = x * y;
return z;
}
main(){
int x = 3, y = 5;
printf("Address of x in main() = %d\n", &x);
printf("Address of y in main() = %d\n", &y);
int product = multiply(x,y);
printf("Product = %d\n", product);
}
/* Output */
Address of x in main() = 6422040
Address of y in main() = 6422036
Address of x in multiply() = 6422000
Address of y in multiply() = 6422008
Product = 15
Since we created stored values in a new location, it costs us memory. Wouldn't it be better if we could perform the same task without wasting space?
Call by reference helps us achieve this. We pass the address of or the reference of the variables to the function which does not create a copy. Using the dereferencing operator *
, we can access the value stored at those addresses.
We can rewrite the above program using call by reference as well.
#include <stdio.h>
int multiply(int *x, int *y){
int z;
z = (*x) * (*y);
return z;
}
main(){
int x = 3, y = 5;
int product = multiply(&x,&y);
printf("Product = %d\n", product);
/* prints "Product = 15" */
}
1. Pointers as Function Arguments
In this section, we will look at various programs where we give int
, char
, arrays and strings as arguments using pointers.
#include <stdio.h>
void add(float *a, float *b){
float c = *a + *b;
printf("Addition gives %.2f\n",c);
}
void subtract(float *a, float *b){
float c = *a - *b;
printf("Subtraction gives %.2f\n",c);
}
void multiply(float *a, float *b){
float c = *a * *b;
printf("Multiplication gives %.2f\n",c);
}
void divide(float *a, float *b){
float c = *a / *b;
printf("Division gives %.2f\n",c);
}
main(){
printf("Enter two numbers :\n");
float a,b;
scanf("%f %f",&a,&b);
printf("What do you want to do with the numbers?\nAdd : a\nSubtract : s\nMultiply : m\nDivide : d\n");
char operation = '0';
scanf(" %c",&operation);
printf("\nOperating...\n\n");
switch (operation) {
case 'a':
add(&a,&b);
break;
case 's':
subtract(&a,&b);
break;
case 'm':
multiply(&a,&b);
break;
case 'd':
divide(&a,&b);
break;
default:
printf("Invalid input!!!\n");
}
}
We created four functions, add()
, subtract()
, multiply()
and divide()
to perform arithmetic operations on the two numbers a
and b
. The address of a
and b
was passed to the functions. Inside the function using *
we accessed the values and printed the result.
Similarly, we can give arrays as arguments using pointer to its first element.
#include <stdio.h>
void greatestOfAll( int *p){
int max = *p;
for(int i=0; i < 5; i++){
if(*(p+i) > max)
max = *(p+i);
}
printf("The largest element is %d\n",max);
}
main(){
int myNumbers[5] = { 34, 65, -456, 0, 3455};
greatestOfAll(myNumbers);
/* Prints :The largest element is 3455" */
}
Since the name of an array itself is a pointer to the first element, we send that as an argument to the function greatestOfAll()
. In the function, we traverse through the array using loop and pointer.
#include <stdio.h>
#include <string.h>
void wish(char *p){
printf("Have a nice day, %s",p);
}
main(){
printf("Enter your name : \n");
char name[20];
gets(name);
wish(name);
}
Here, we pass in the string name
to wish()
using a pointer and print the message.
2. Pointers as Function Return
#include <stdio.h>
int* multiply(int *a, int *b){
int c = *a * *b;
return &c;
}
main(){
int a= 3, b = 5;
int *c = multiply (&a,&b);
printf("Product = %d",*c);
}
The function multiply()
takes two pointers to int
. It returns a pointer to int
as well which stores the address where the product is stored.
It is very easy to think that the output would be 15. But it is not!!!
When multiply()
is called, the execution of main()
pauses and memory is now allocated for execution of multiply()
. After its execution is completed, the memory allocated to multiply()
is deallocated.
Therefore, though c
( local to main()
) stores the address of the product. The data there is not guaranteed since that memory has been deallocated.
So does that mean pointers cannot be returned by a function? No!!!
We can do two things. Either store the address in the heap or global section or declare the variable to be static
so that their values persist.
Static variables can simply be created by using keyword static
before data type while declaring the variable.
To store addresses in heap, we can use library functions malloc()
and calloc()
which allocate memory dynamically. The following programs will explain both the methods.
#include <stdio.h>
#include <stdlib.h>
/* Using malloc() */
int* multiply(int *a, int *b){
int *c = malloc(sizeof(int));
*c = *a * *b;
return c;
}
main(){
int a= 3, b = 5;
int *c = multiply (&a,&b);
printf("Product = %d",*c);
}
/* Using static keyword */
#include <stdio.h>
int* multiply(int *a, int *b){
static int c;
c = *a * *b;
return &c;
}
main(){
int a= 3, b = 5;
int *c = multiply (&a,&b);
printf("Product = %d",*c);
}
Both methods return the output as 15.
3. Pointer to Function
Like pointer to different data types, we also have a pointer to function as well. A pointer to function or function pointer stores the address of the function. Though it doesn't point to any data. It points to the first instruction in the function.
The syntax for declaring a pointer to function is -
/* Declaring a function */
returnType functionName(parameterType1, pparameterType2, ...);
/* Declaring a pointer to function */
returnType (*pointerName)(parameterType1, parameterType2, ...);
pointerName = &functionName; /* or pointerName = functionName; */
The below example will show how you can use it.
int* multiply(int *a, int *b)
{
int *c = malloc(sizeof(int));
*c = *a * *b;
return c;
}
main()
{
int a=3,b=5;
int* (*p)(int*, int*) = &multiply; /* or int* (*p)(int*, int*) = multiply; */
int *c = (*p)(&a,&b); /* or int *c = p(&a,&b); */
printf("Product = %d",*c);
}
The declaration for the pointer p
to function multiply()
can be read as ( following operator precedence) - p
is a pointer to function with two int
eger pointers ( or two pointers to int
) as parameters and returning a pointer to int
.
Since the name of the function is also a pointer to the function, the use of &
is not necessary. Also removing *
from the function call doesn't affect the program.
4. Array of Pointers to Functions
We have already seen how to create an array of pointers to int
, char
etc. Similarly, we can create an array of pointers to function.
In this array, every element will store an address of a function, where all the functions are of the same type. That is, they have the same type and no. of parameters and return types.
We will modify the program discussed here. We will store the addresses of add()
, subtract()
, multiply()
and divide()
in an array make a function call through subscipts.
#include <stdio.h>
void add(float *a, float *b){
float c = *a + *b;
printf("Addition gives %.2f\n",c);
}
void subtract(float *a, float *b){
float c = *a - *b;
printf("Subtraction gives %.2f\n",c);
}
void multiply(float *a, float *b){
float c = *a * *b;
printf("Multiplication gives %.2f\n",c);
}
void divide(float *a, float *b){
float c = *a / *b;
printf("Division gives %.2f\n",c);
}
main(){
printf("Enter two numbers :\n");
float a,b;
scanf("%f %f",&a,&b);
printf("What do you want to do with the numbers?\nAdd : a\nSubtract : s\nMultiply : m\nDivide : d\n");
char operation = '0';
scanf(" %c",&operation);
void (*p[])(float* , float*) = {add,subtract,multiply,divide};
printf("\nOperating...\n\n");
switch (operation) {
case 'a':
p[0](&a,&b);
break;
case 's':
p[1](&a,&b);
break;
case 'm':
p[2](&a,&b);
break;
case 'd':
p[3](&a,&b);
break;
default:
printf("Invalid input!!!\n");
}
}
The declaration here can be read as - p
is an array of pointer to functions with two float
pointers as parameters and returning void.
5. Pointer to Function as an Argument
Like any other pointer, function pointers can also be passed to another function, therefore known as callback function or called function. The function to which it is passed is known as calling function.
A better way to understand would be to look at qsort()
, which is an inbuilt function in C. It is used to sort an array of integers, strings, structures, etc. The declaration for qsort()
is -
void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void *));
qsort()
takes four arguments -
- a
void
pointer to the start of an array - no. of elements
- size of each element
- a function pointer that takes in two
void
pointers as arguments and returns anint
The function pointer points to a comparison function that returns an integer that is greater than, equal to or less than zero, if the first argument is respectively greater than, equal to or less than the second argument.
The following program showcases its usage.
#include <stdio.h>
#include <stdlib.h>
int compareIntegers(const void *a, const void *b)
{
const int *x = a;
const int *y = b;
return *x - *y;
}
main(){
int myArray[] = {97,59,2,83,19,97};
int numberOfElements = sizeof(myArray) / sizeof(int);
printf("Before sorting - \n");
for(int i = 0; i < numberOfElements; i++)
printf("%d ", *(myArray + i));
qsort(myArray, numberOfElements, sizeof(int), compareIntegers);
printf("\n\nAfter sorting - \n");
for(int i = 0; i < numberOfElements; i++)
printf("%d ", *(myArray + i));
}
/* Output */
Before sorting -
97 59 2 83 19 97
After sorting -
2 19 59 83 97 97
Since a function name is itself a pointer, we can write compareIntegers
as the fourth argument.
In this article, we saw the various use cases of pointers with functions. How pointers can be both arguments as well as return, albeit with a bit of caution in latter. We also extended our idea about pointers being used with functions to function pointers and how they are used in C.
The edits suggested by @pentacular have been included. Thanks to him/her.
Top comments (5)
A bit confused by this assertion.
x and y in main are not parameters -- they're variables.
In the call to multiply their values are passed as arguments (not parameters) to multiply.
Within multiply they initialize the parameters x and y.
There is no call by reference in C.
Here you are passing pointer values by value.
You are not giving arrays as arguments -- it is not possible to produce an array typed value in C, and since C passes by value, it just can't happen.
An array in C evaluates to a pointer to its first element, and that's what you're passing.
You could pass a pointer to the array instead, which would look like this.
This example is illegal as you are trying to initialize an int * with an int.
It does not point to the first instruction in the function.
As you've noted it doesn't point to any data -- C has a segmented harvard memory model, where functions are opaque handles.
C also has no concept of instruction.
You can make this much simpler by using typedef.
Huge thanks for pointing out the mistakes. 🙏
You're welcome. :)
C is often taught in a very confusing fashion.
Nice article! Great reminder of the things I was thought in my first year at university.
Thanks! 🙏