DEV Community

Cover image for Pointers In C - Functions
Srijan
Srijan

Posted on • Edited on

Pointers In C - Functions

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 integer 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 -

  1. a void pointer to the start of an array
  2. no. of elements
  3. size of each element
  4. a function pointer that takes in two void pointers as arguments and returns an int

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)

Collapse
 
pentacular profile image
pentacular

In the function call multiply(x,y), we passed the value of x and y ( of main()), which are actual parameters, to multiply().

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.

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.

There is no call by reference in C.

Here you are passing pointer values by value.

Similarly, we can give arrays as arguments using pointers.

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.

void foo(int (*p)[5]) {
  // *p is the array we passed, hurrah.
}

int main() {
  int a[5];
  foo(&a);
}

int* multiply(int *a, int *b){
int *c = *a * *b;
return c;
}

This example is illegal as you are trying to initialize an int * with an int.

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.

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.

The syntax for declaring a pointer to function is -

You can make this much simpler by using typedef.

typedef int foo(char);

foo *bar; // A pointer to an int(char) type function.
Collapse
 
its_srijan profile image
Srijan • Edited

Huge thanks for pointing out the mistakes. 🙏

Collapse
 
pentacular profile image
pentacular

You're welcome. :)

C is often taught in a very confusing fashion.

Collapse
 
kriska profile image
Kristina Gocheva

Nice article! Great reminder of the things I was thought in my first year at university.

Collapse
 
its_srijan profile image
Srijan

Thanks! 🙏