Higher order functions in C

Published on September 02, 2016
under Blog

Abstract

It's not a secret that for almost all people attempting to learn C pointers are a grey area. I personally was struggling quite a lot understanding them but I was saved by a huge amount of amazing guides on pointer usage you can find online. That said, I still feel like implementation of higher order functions in C deserves more exposure that it currently has and this article is my attempt to contribute to this cause.

Note: In this article I'm gonna assume that you have some experience with C and know what pointers are. Additionally, I will assume you know how to write and compile .c files.

What are higher order functions?

Higher order functions take other functions as their arguments or return new functions when they terminate. It might sound confusing especially if you've never seen it being done before but in reality it is really quite simple. Consider the pseudocode below:

To summarise: apply() function above takes 2 parameters: a string and an another function. Then, as the name implies, it applies the operation to the supplied string and returns whatever comes out. This results in a very interesting form of polymorphism: our apply() function can take any operation regardless of what it does as long as it accepts a string as an argument and returns the string afterwards.

The example above looks at the scenario when a function takes other functions as its arguments. Now let's take a look at functions that return new functions:

As seen above, multiplyOperation() returns a new function that changes its behaviour depending on what value for the coefficient has been supplied to multiplyOperation(). Keep in mind that the examples above are just pseudocode to give you an idea of what higher order functions are about and C implementation will not necessarily look the same.

Function pointers in C

To achieve the functionality discussed in the previous section we're gonna use function pointers, which are pretty straightforward and shouldn't take you too long to wrap your head around. Before we start, remind yourself of these 2 things:

You can get the address of a variable using the & operator, like so: int *a = &b; (a can now be used as a pointer to b). That said, when referencing a function you are allowed to drop the & operator. If you still don't have that much experience with pointers, I recommend leaving it in.

Before you can access the variable a pointer is pointing to, you must dereference said pointer. Let's take integer pointer a from the line above - if I want to use it in a calculation, I need to dereference it using the * operator: int c = 3 + *a;.

I tried to add comments to all of the important parts but I want to discuss the map() function definition separtely. Let's break it down: int* map(int (*function)(int), int* array, int length) {...}

As in any other function, the first part (int*) denotes the return type of our map(). In this particular case it's a pointer to an integer array.

map is simply the function name, nothing fancy here.

Now the interesting part - function parameters. There are of them:

int (*function)(int) Defines a parameter called function that is a pointer to a function accepting one integer and returning an integer.

int* array A pointer to an integer array over which we'll be iterating.

int size Integer storing the size of the above array.

Now, we can use our newly defined function to "transform" integer arrays using some unary function. Unfortunately we did not make it truly polymorphic, that is it only works with integers but there is a very good reason for it: polymorphism in C is quite a complicated topic and it goes beyond the scope of this article. If you're interested in an implementation of the map() function that supports generic types, check out this.

Functions returning functions

This section will look at the other type of higher order functions, namely functions that return other functions after they terminate. Below you can see the implementation of a function that does exactly that: it returns binary arithmetic operations based on the value of the type parameter supplied to it.

Take a look at the function definition for getOperation(): Its return type is "outside" and the actual definition that matches the function body is "inside" the parentheses. Let's break it down:

getOperation // Name of the function
getOperation(int type) // Parameters the function is expecting
(*getOperation(int type))(int, int) // Parameters of the returned function
int (*getOperation(int type))(int, int) // Return type of the returned function

Conclusion

The aim of this article is to give you a basic idea of how to define functions that accept other functions as parameters and return new functions. If after having read it you feel like you can do it, then this article has successfully fulfilled its purpose. Otherwise, feel free to comment below for help.

End of Article

Timur Kuzhagaliyev Author

I'm a Computer Science student at UCL, spending a year in Caltech. Check out my GitHub to see what I'm up to. Feel free to like and share: