Programs as Data: Function Pointers

来源:互联网 发布:网络丝和低弹丝的区别 编辑:程序博客网 时间:2024/06/05 16:51

Forwarded from: http://www.cprogramming.com/tutorial/function-pointers.html

 

A function pointer is a variable that stores the address of a function thatcan later be called through that function pointer. This is useful becausefunctions encapsulate behavior. For instance, every time you need aparticular behavior such as drawing a line, instead of writing out a bunch ofcode, all you need to do is call the function. But sometimes you would liketo choose different behaviors at different times in essentially the same pieceof code. Read on for concrete examples.

Example Uses of Function Pointers

Functions as Arguments to Other Functions

If you were to write a sortroutine, you might want to allow the function's caller to choose the orderin which the data is sorted; some programmers might need to sort the data inascending order, others might prefer descending order while still others maywant something similar to but not quite like one of those choices. One way tolet your user specify what to do is to provide a flag as an argument to thefunction, but this is inflexible; the sort function allows only a fixed set ofcomparison types (e.g., ascending and descending).

A much nicer way of allowing the user to choose how to sort the data is simplyto let the user pass in a function to the sort function. This function mighttake two pieces of data and perform a comparison on them. We'll look at thesyntax for this in a bit.

Callback Functions

Another use for function pointers is setting up "listener" or "callback"functions that are invoked when a particular event happens. The function iscalled, and this notifies your code that something of interest has takenplace.

Why would you ever write code with callback functions? You often see it whenwriting code using someone's library. One example is when you're writing codefor a graphical user interface (GUI). Most of the time, the user willinteract with a loop that allows the mouse pointer to move and that redrawsthe interface. Sometimes, however, the user will click on a button or entertext into a field. These operations are "events" that may require a responsethat your program needs to handle. How can your code know what's happening?Using Callback functions! The user's click should cause the interface to calla function that you wrote to handle the event.

To get a sense for when you might do this, consider what might happen if youwere using a GUI library that had a "create_button" function. It might takethe location where a button should appear on the screen, the text of thebutton, and a function to call when the button is clicked. Assuming for themoment that C (and C++) had a generic "function pointer" type called function,this might look like this:

void create_button( int x, int y, const char *text, function callback_func );

Whenever the button is clicked, callback_func will be invoked. Exactly whatcallback_func does depends on the button; this is why allowing thecreate_button function to take a function pointer is useful.

Function Pointer Syntax

The syntax for declaring a function pointer might seem messy at first, butin most cases it's really quite straight-forward once you understandwhat's going on. Let's look at a simple example:

void (*foo)(int);

In this example, foo is a pointer to a function taking one argument, aninteger, and that returns void. It's as if you're declaring a function called"*foo", which takes an int and returns void; now, if *foo is a function, thenfoo must be a pointer to a function. (Similarly, a declaration like int *xcan be read as *x is an int, so x must be a pointer to an int.)

The key to writing the declaration for a function pointer is that you're justwriting out the declaration of a function but with (*func_name) where you'dnormally just put func_name.

Reading Function Pointer Declarations

Sometimes people get confused when more stars are thrown in:

 

Here, the key is to read inside-out; notice that the innermost element of theexpression is *foo, and that otherwise it looks like a normal functiondeclaration. *foo should refer to a function that returns a void * and takesan int *. Consequently, foo is a pointer to just such a function.

Initializing Function Pointers

To initialize a function pointer, you must give it the address of a functionin your program. The syntax is like any other variable:

 

(Note: all examples are written to be compatible with both C and C++.)

Using a Function Pointer

To call the function pointed to by a function pointer, you treat thefunction pointer as though it were the name of the function you wish to call.The act of calling it performs the dereference; there's no need to do ityourself:

 

Note that function pointer syntax is flexible; it can either look like mostother uses of pointers, with & and *, or you may omit that part of syntax.This is similar to how arrays are treated, where a bare array decays to apointer, but you may also prefix the array with & to request its address.

Function Pointers in the Wild

Let's go back to the sorting example where I suggested using a functionpointer to write a generic sorting routine where the exact order could bespecified by the programmer calling the sorting function. It turns out thatthe C function qsort does just that.

From the Linux man pages, we have the following declaration for qsort (fromstdlib.h):

  

Note the use of void*s to allow qsort to operate on any kind of data (in C++,you'd normally use templates forthis task, but C++ also allows the use of void* pointers) because void*pointers can point to anything. Because we don't know the size of theindividual elements in a void* array, we must give qsort the number ofelements, nmemb, of the array to be sorted, base, in addition to the standardrequirement of giving the length, size, of the input.

But what we're really interested in is the compar argument to qsort: it's afunction pointer that takes two void *s and returns an int. This allowsanyone to specify how to sort the elements of the array base without having towrite a specialized sorting algorithm. Note, also, that compar returns anint; the function pointed to should return -1 if the first argument is lessthan the second, 0 if they are equal, or 1 if the second is less than thefirst.

For instance, to sort an array of numbers in ascending order, we could writecode like this:

 

Using Polymorphism and Virtual Functions Instead of Function Pointers(C++)

You can often avoid the need for explicit function pointers by using virtualfunctions. For instance, you could write a sorting routine that takes apointer to a class that provides a virtual function called compare:

 

inside cpp_qsort, whenever a comparison is needed, compar->compare shouldbe called. For classes that override this virtual function, the sort routinewill get the new behavior of that function. For instance:

 

and then you could pass in a pointer to an instance of the AscendSorter tocpp_qsort to sort integers in ascending order.

But Are You Really Not Using Function Pointers?

Virtual functions are implemented behind the scenes using function pointers,so you really are using function pointers--it just happens that the compilermakes the work easier for you. Using polymorphism can be an appropriatestrategy (for instance, it's used by Java), but it does lead to the overheadof having to create an object rather than simply pass in a function pointer.

Function Pointers Summary

Syntax

Declaring

Declare a function pointer as though you were declaring a function, exceptwith a name like *foo instead of just foo:

 

Initializing

You can get the address of a function simply by naming it:

 

or by prefixing the name of the function with an ampersand:

 

Invoking

Invoke the function pointed to just as if you were calling a function.

 

or you may optionally dereference the function pointer before calling thefunction it points to:

 

Benefits of Function Pointers

  • Function pointers provide a way of passing around instructions for how to do something
  • You can write flexible functions and libraries that allow the programmer to choose behavior by passing function pointers as arguments
  • This flexibility can also be achieved by using classes with virtual functions
原创粉丝点击