REN

Ph.D. in Computer Science at Rutgers University

Talking about C pointer - Function Pointer and Polymorphism in C

Function pointer itself is a pointer. Instead of other types of pointer that point to a specific type, a function pointer points to the address of a function that could later be called via this pointer. The feature of function pointer could be used to present callback function and polymorphism.

Basic Syntex and Example

The syntex of delaring a function pointer is like following:

void (*foo1)(int);
int* (*foo2)(double*, void*);

Function pointer foo1 indicates that the function whose return type is void and take 1 argument of integer is foo1 could point to. Similarly, foo2 could point to the function that return type is int*, and take 2 argument of double* and void*.

Now, let's look at an example on how to use function pointer.

/* test.c */
#include <stdio.h>

int weighed_add(int a, int b) {
	int r = a * 2 + b * 3;
	return r;
}

int main() {
	int (*fp)(int, int) = &weighed_add;
	int r = fp(1, 1);
	printf("r = %d\n", r);
	return 0;
}

Let's compile this program in GCC:

gcc -m32 -o test test.c

The result of those codes are:

r = 5

Callback Function

Callback function, in the programmers' perspective, is a "passive" procedure call. A common function call happens at the position the programmer explicitly want it to be called. A callback function is invoked when a particular event happens, so it not decided by the programmer.

The concept seems to be obscure, so let me explain it in another aspect. Assuming you're writing a C program, and you want to invoke system call read() in Line 4. When this program is running and Line 4 is being executed, system call read() is invoked. Invoking read() is an "active" function call which means it is programmer that invokes the system call. Later, you want to write a signal handler in the previous program; When a SIGSEGV signal is raised, you want Linux to execute your function handler(). So when this program access an illegal memory address, your function handler() is invoked. In this case, handler() is a callback function because handler() is not invoked directly in your code.

Function pointer could be used to implement callback function. Now let's look at an example.

/* test.c */
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

struct sigaction act;

void sig_handler(int signo, siginfo_t *si, void *ucontext) {
	printf("Got Segmentation Fault %d\n",signo);
}
 
int main () {
	act.sa_sigaction = sig_handler;
	act.sa_flags = SA_SIGINFO;

	sigaction(SIGSEGV, &act, NULL);

	int *a = 0x01;
	int b = *a;   
                  
	return 0;
}

Let's compile this program in GCC:

gcc -m32 -o test test.c

The result of those codes are:

Got Segmentation Fault 11

In the code above, function sig_handler() is not invoked explicitly. It was registered in sigaction(). When an illegal memory access happens, SIGSEGV signal is raised and sig_handler() is invoked. Let's see the definition of sigaction() and struct sigaction:

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

struct sigaction {
	void     (*sa_handler)(int);
	void     (*sa_sigaction)(int, siginfo_t *, void *);
	sigset_t   sa_mask;
	int        sa_flags;
	void     (*sa_restorer)(void);
};

In struct sigaction, member sa_sigaction is a function pointer that could pointer to a handler function defined by programmer, and programmer-defined handler function is a callback function.


Polymorphism

As we know, the three key features of Object-Oriented Language are Encapsulation, Inheritance and Polymorphism. The following Java code shows a typical polymorphism:

/* test.java */
abstract class Animal {
	String name;
	abstract void talk();
}

class Cat extends Animal {
	void talk() {
		System.out.println("Meow!");
	}
}

class Dog extends Animal {
	void talk() {
		System.out.println("Woof!");
	}
}

public class test {
	public static void main(String[] args) {
		Cat cat = new Cat();
		Dog dog = new Dog();
		cat.talk();
		dog.talk();
	}
}

Let's compile this program in JDK 1.7:

javac test.java

The result of those codes are:

Meow!
Woof!

The class Animal has an abstract method talk(), different sub classes could have their own talk(). By using function pointer, we could also easily implement such mechanism.

/* test.c */
#include <stdio.h>

typedef struct Animal {
	char name[10];
	void (*talk)();
} Animal;

void meow() {
	printf("Meow!\n");
}

void woof() {
	printf("Woof!\n");
}

Animal cat = {"Alex", &meow};
Animal dog = {"Jack", &woof};

int main(void) {
	cat.talk();
	dog.talk();
	return 0;
}

Let's compile this program in GCC:

gcc -m32 -o test test.c

The result of those codes are:

Meow!
Woof!

By using function pointer, different objects of a struct could have their own behaviors, which enabless C language some features of polymorphism. But a difference in the default visibility exists: class members are by default private, whereas struct members are by default public.

Conclusion

C pointer series end in this post. I tried to illustrates basic concepts and usage on C pointers because pointer, in my opinion, is the hardest and most interesting part in C. However, I cannot cover every detailed knowledge in C pointer. If you're interested in programming in C for further steps. I strongly recommend two books: Pointers on C by Kenneth A. Reek and C++: The Core Language - A Foundation for C Programmers by Doug Brown, Gregory Satir.