Function Pointers === # What are Function Pointers Function pointers are **references** to memory addresses of functions. * It's ++still a pointer++. So it's **4-bytes** large (on a 32-bit machine) and stores a **memory address** (some hex value). * When compiled, the instructions for how to execute a function is stored in a certain area of memory. * A **function pointer** stores the **value** of the memory address of the **start** of where the instructions for a function is stored. # Declaring with Typedefs ### How to declare without typedefs Remember when you declare a pointer, you have to specify the type of data object you are pointing to. ```C int *pointer; char *string; data_type *name; // read as "name" is a pointer to "data_type" ``` So if you want to declare a function pointer, you have to specify the ++type of function++ your pointer is pointing to. So something like: ```C type_of_function *my_func_pointer; ``` So what is the type of a function? Remember how if you want to define a function below where you use it in `main`, you have to stick a **function prototype** above `main`? This is so that the compiler knows the ++type++ of a function when it comes across its name in `main`. If you don't have this function prototype and stick the line `int num = foo(3)` in your code, your compiler is going to stare at this `foo` and complain because it doesn't know what `foo` is. So we know that to make the compiler happy, we have to include the **return type** and **argument types** in the function prototype, as well as the **name** of the function this `(return type, argument type)` pair is associated with. ```C int foo(int num); void foo2(int, int, char*); // don't have to specify names of arguments, just need types // general form return_type name_of_function(arg_1_type, arg_2_type, ...); ``` With this in mind, we are ready to declare a function pointer! ```C int (*fp)(int); // fp is a pointer to a function that takes an int as an argument and returns an int // general form return_type (*name_of_func)(arg_1_type, arg_2_type); ``` ### Motivation for typedefs That's not too bad... It kind of just looks like a function prototype with a `*` in front of the function name to signal that this is a pointer. So **why do we usually typedef a function pointer**? Well, things can start to get a bit messy if we want to declare ++lots of functions pointers++ of the same type. ```C int (*fp)(int); int (*otherFunc)(int); int (*funcA)(int); int (*funcE)(int); int (*longWindedName)(int); ``` Can you see how this is a little bit hard to read? To really drive the point home, consider this function **prototype** of a `signal()` function. ```C void (*signal(int, void(*)(int)))(int); ``` How long does it take you to figure out what this line of code is doing? * `signal` is a pointer to a function * arguments = `int`, function pointer to a function that has an `int` arg and a `void` return value * return values = a function pointer to a function that has an `int` arg and a `void` return value If we `typedef`ed some of the confusing parts, it would be a lot more readable ```C // SignalHandler is a function that take in an integer argument called signum and returns void typedef void (*SignalHandler)(int signum); // the signal prototype is now SignalHandler signal(int signum, SignalHandler handler); ``` Benefits of introducing this typedef * the `void(*)(int)` that we saw two of now make a lot more sense as we gave it a ++meaningful name++ * there isn't a mess of parentheses and I can decode what's going on pretty quickly So what would its function pointer look like now? ```C typedef SignalHandler (*signal_pointer)(int, SignalHandler); signal_pointer my_signal = &signal; // my_signal is a pointer to a function of type signal, and it is // assigned the memory address of a function signal defined elsewhere in the file ``` Now everything is a lot more **readable**. Also, clarity helps to **reduce the chance of bugs**. **Code complexity** is the bane of every programmer's existence as it is exactly how bugs are introduced. The simpler your code, the better. `typedef` is the tool we use to do that here. ### This typedef syntax looks weird Before, when we `typedef`ed something, it kind of looked like this ```C typedef struct node Node; ``` We knew that the right-most "thing" is the name of our new alias, and everything else is the original object that we mean when we say `Node`. Function pointer typedefs are a bit different since you have to specify a ++pair++ of types, a `return type` and `argument types`. So the **name** is actually squashed ==**between**== these two. ```C typedef int (*somefunc_type)(int, int); // general form typedef return_type (*function_type_name)(arg1_type, arg2_type, ...); ``` So if we want to declare `fp` as a pointer to a function of type `return_type` and (`arg1_type`, `arg2_type`, ...), we say `fp` is of type `function_type_name`. ```C somefunc_type fp = &foo; // fp is a pointer to the foo function ``` So just remember that the syntax is a little bit funny since we are using a `typedef` to encompass **more than one type**. # Why do I care about function pointers? Sometimes we have a function that is super useful and that we might want to use on a whole bunch of data types. But as we know, functions in `C` require explicit data types for its return and arguments. I don't want to write 10 diff functions for the different `structs` and data types I want to use this function with. I want to make it **generic**. Some other languages (i.e. Java) have built-in support for generic types, but `C` does not. So we have to use **pointers** to try and introduce some of this ++generic functionality++. (Just remember, in `C`, everything is a pointer...) # TL;DR Function pointers are useful for plugging in functions into functions. Declare them with typedefs to make things more readable. We don't deal with anything *too* complicated in COMP2521, but we're teaching you good practices for crazy `signal()` functions you may see in the future. ```C // typedef the pointer type typedef int (*t_some_func)(int, int); // declare a function pointer called afunc t_somefunc afunc = &product; // use your function pointer int x2 = (*afunc)(123, 456); // older syntax // this is the same as int x2 = afunc(123, 456); // newer syntax ``` Yes, the `typedef` looks a bit weird because you are linking more than one data type to a name (`t_some_func`). Be careful with your typedefs. # References * [Ashesh's Generic ADTs in C Notes](https://webcms3.cse.unsw.edu.au/static/uploads/course/COMP2521/17s2/07093d2c4bbd2e92b94adb285fdc2368612144e074121a5adf685b7ffd26b6db/GenericADT-17s2.pdf) * [Motivation for Typedef Function Pointer StackOverflow](https://stackoverflow.com/questions/4295432/typedef-function-pointer) * [Signal Example of Typedef Function Pointer StackOverflow](https://stackoverflow.com/questions/1591361/understanding-typedefs-for-function-pointers-in-c)