Lecture 9: Function Pointers

We will learn how to pass functions around in other functions via pointers.

That is, we will...


memset

A function that sets a specified amount of bytes at one address to a certain value. Basically, goes to s and fills in n values there with c.

Bubble sort

Let us say we want to implement a bubble sort algorithm in C. We can do this with ints as normal:

void bubble_sort_int(int *arr, int n) {
	while (true) {
		bool swapped = false;
		for (int i = 1; i < n; i++) {
			if (arr[i - 1] > arr[i]) {
			swapped = true;
			swap_int(&arr[i - 1], &arr[i]);
			}
		}
		if (!swapped) {
			return;
		}
	}
}

But how can we make this generic? We can first at least make the parameters generic. We also need to be able to locate the iith element of an array for bubble sort. How can we do this with generic pointers? Remember the key ideas behind generic pointer arithmetic from last lecture. Then, we can call our generic swap function we made last lecture. Now we have:

void bubble_sort(void *arr, int n, int elem_size_bytes) {
	while (true) {
		bool swapped = false;
		for (int i = 1; i < n; i++) {
			void *p_prev_elem = (char *)arr + (i - 1) * elem_size_bytes;
			void *p_curr_elem = (char *)arr + i * elem_size_bytes;
			if (*p_prev_elem > *p_curr_elem) {
				swapped = true;
				swap(p_prev_elem, p_curr_elem, elem_size_bytes);
			}
		}
		if (!swapped) {
			return;
		}
	}
}

But there is a slight problem: we can't use if (*p_prev_elem > *p_curr_elem). We cannot compare certain data types using > (i.e. strings). In addition we cannot even deference void *'s.

So how do we write code that we can compare two elements of any type? We must rely on the client to supply additional information. We can use function pointers for this.

Function pointers

Introduction

You can think of a function as a set of instructions to transform a certain input. During compile time, C actually represents functions in memory as a set of instructions. Thus, we can have a pointer in our code to these instructions. This is the idea behind function pointers.

Back to the example

Now back to the above example. We know the caller has the data so they know the type. Thus, they should supply us a function that we can use to compare those certain types. To do this, they pass in a function via a parameter:

void bubble_sort(void *arr, int n, int elem_size_bytes, function compare_fn); 

But in C the syntax isn't actually all that clean (the above is just for pedagogical purposes). It is really more like this:

void bubble_sort(void *arr, int n, int elem_size_bytes,
bool (*compare_fn)(void *a, void *b)); 

Syntax

The general syntax for declaring a function pointer is as follows:

[return type] (*[name])([parameters])

As an example:

bool (*compare_fn)(void *a, void *b)

If you really audit the syntax, it makes sense.

Our goal was to write a generic bubble sort function. This also means that the compare function being passed in must be written generically meaning that it takes in void * pointers to data (i.e. the elems of the array you are comparing) rather than the data itself.

As an aside, in C it is common to write comparison functions like this that return an int and not a boolean where:

This tells us a little more information which we can use if needed.

And we finally have our generic bubble sort implementation:

void bubble_sort(void *arr, int n, int elem_size_bytes,
	int (*compare_fn)(void *a, void *b)) {
		while (true) {
			bool swapped = false;
			for (int i = 1; i < n; i++) {
				void *p_prev_elem = (char *)arr + (i - 1) * elem_size_bytes;
				void *p_curr_elem = (char *)arr + i * elem_size_bytes;
				if (compare_fn(p_prev_elem, p_curr_elem) > 0) {
					swapped = true;
					swap(p_prev_elem, p_curr_elem, elem_size_bytes);
				}
			}
		if (!swapped) {
			return;
		}
	}
}