Programs as Data: Function Pointers
A function pointer is a variable that stores the address of a function that can later be called through that function pointer. This is useful because functions encapsulate behavior. For instance, every time you need a particular behavior such as drawing a line, instead of writing out a bunch of code, all you need to do is call the function. But sometimes you would like to choose different behaviors at different times in essentially the same piece of code. Read on for concrete examples.
Example Uses of Function Pointers
Functions as Arguments to Other Functions
If you were to write a sort routine, you might want to allow the function's caller to choose the order in which the data is sorted; some programmers might need to sort the data in ascending order, others might prefer descending order while still others may want something similar to but not quite like one of those choices. One way to let your user specify what to do is to provide a flag as an argument to the function, but this is inflexible; the sort function allows only a fixed set of comparison types (e.g., ascending and descending).
试想你打算写一个排序函数,你也希望这个函数能够实现不同的排序方式,有的程序员需要将数据以升序排列,而有的却需要将数据以降序排列,一个更佳的办法就是让程序员将一个标志作为形参传入排序函数以指定排序函数的排序行为
A much nicer way of allowing the user to choose how to sort the data is simply to let the user pass in a function to the sort function. This function might take two pieces of data and perform a comparison on them. We'll look at the syntax 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 is called, and this notifies your code that something of interest has taken place.
使用函数指针的另一个用途就是设置“监听者”或者是“回调函数”,当触发了某一些特殊事件后,其函数指针指向的函数就会被调用。函数调用时,它会通知你,有趣的事情已经发生了
Why would you ever write code with callback functions? You often see it when writing code using someone's library. One example is when you're writing code for a graphical user interface (GUI). Most of the time, the user will interact with a loop that allows the mouse pointer to move and that redraws the interface. Sometimes, however, the user will click on a button or enter text into a field. These operations are "events" that may require a response that 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 call a function that you wrote to handle the event.
为什么要去使用回调函数呢?通常你会看到某些代码会使用库函数。比如说,编写一个GUI。很多时候,用户会不间断的移动鼠标,因此界面也必须要不断地进行绘制。也有时候,用户点击按钮或者是输入了一些文本,这些“事件”都需要程序去处理并应该都能得到相应的反馈。但是在你的代码中又是怎样知道事件的发生呢?使用回调函数!比如说,用户点击了按钮,导致了这个按钮接口去调用处理该事件的代码
To get a sense for when you might do this, consider what might happen if you were using a GUI library that had a "create_button" function. It might take the location where a button should appear on the screen, the text of the button, and a function to call when the button is clicked. Assuming for the moment 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 what callback_func does depends on the button; this is why allowing the create_button function to take a function pointer is useful.
Function Pointer Syntax
The syntax for declaring a function pointer might seem messy at first, but in 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, an integer, 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, then foo must be a pointer to a function. (Similarly, a declaration like int *x can be read as *x is an int, so x must be a pointer to an int.)
在这个例子中,foo是一个指针,它指向一个含有一个整型参数,返回值是void类型的函数。这好比你声明了一个名为“*foo”的函数,这个函数带有一个整型函数,并且返回值为void型,如果说*foo是一个函数,那么foo必然就是一个指向函数的指针。(同理,一个像 int *x 的声明可以读成 *x 是一个整型变量,因此 x 必然就是指向一个int型变量的指针)
The key to writing the declaration for a function pointer is that you're just writing out the declaration of a function but with (*func_name) where you'd normally just put func_name.
声明一个函数指针的关键就是,当你声明完一个函数时,将函数名改为以 (*func_name) 形式表示
Reading Function Pointer Declarations
Sometimes people get confused when more stars are thrown in:
void *(*foo)(int *);
Here, the key is to read inside-out; notice that the innermost element of the expression is *foo, and that otherwise it looks like a normal function declaration. *foo should refer to a function that returns a void * and takes an int *. Consequently, foo is a pointer to just such a function.
在这里,阅读函数指针的声明的关键是“从里到外”地读,上面语句中“最里面”的元素表达式是“*foo”(从左到右去读,注意到第一个括号,以区分它并不是一个简单的函数声明),否则它将会看成是一个简单的函数声明,*foo指的是一个带有一个int*类型的形参,并且返回值是void*类型的函数,因此,foo就是指向这样一个函数的指针(foo是一个函数指针,这个指针指向一个函数,这个函数含有一个int*类型的形参,并且这个函数的返回值是void*类型)
Initializing Function Pointers
To initialize a function pointer, you must give it the address of a function in your program. The syntax is like any other variable:
为了初始化一个函数指针,你必须得为这个指针指定一个函数的地址(函数的地址是函数名,指的是函数体中第一条指令的地址)
#include <stdio.h>
void my_int_func(int x)
{
printf( "%d\n", x );
}
int main()
{
void (*foo)(int);
/* the ampersand is actually optional */
foo = &my_int_func;
return 0;
}
(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 the function 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 it yourself:
当通过函数指针去调用函数时,你完全可以将这个函数指针以函数调用的方式去调用该指针指向的函数,当然,通过接引用的方式去调用函数也是允许的,只不过是你没有必要这样去做
#include <stdio.h>
void my_int_func(int x)
{
printf( "%d\n", x );
}
int main()
{
void (*foo)(int);
foo = &my_int_func;
/* call my_int_func (note that you do not need to write (*foo)(2) ) */
foo( 2 );
/* but if you want to, you may */
(*foo)( 2 );
return 0;
}
Note that function pointer syntax is flexible; it can either look like most other 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 a pointer, 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 function pointer to write a generic sorting routine where the exact order could be specified by the programmer calling the sorting function. It turns out that the C function qsort does just that.
回到sorting那个例子,在那里我建议使用一个函数指针指向一个通用的排序函数,所谓的通用排序函数,就是可以通过一个函数调用来完成不同的排序行为(升序和降序),而事实上,C语言中的快速排序函数qsort正是基于这个原则而设计的
From the Linux man pages, we have the following declaration for qsort (from stdlib.h):
void qsort(void *base, size_t nmemb, size_t size,
int(*compar)(const void *, const void *));
Note the use of void*s to allow qsort to operate on any kind of data (in C++, you'd normally use templates for this 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 the individual elements in a void* array, we must give qsort the number of elements, nmemb, of the array to be sorted, base, in addition to the standard requirement of giving the length, size, of the input.
注意参数类型使用的是空指针void*类型,这说明快排函数qsort接受任意类型的参数(在C++中,你可以使用模板,同样也可以使用空指针),因为空指针void*可以指向任意的类型(使用时只需要进行类型转换).由于并不知道空指针指向的数组的大小,因此qsort要求指定数组单个元素的大小nmemb,以及待排序的数组基地址base,整个数组的大小size,此外,还必须指定排序的方式,是升序还是降序
But what we're really interested in is the compar argument to qsort: it's a function pointer that takes two void *s and returns an int. This allows anyone to specify how to sort the elements of the array base without having to write a specialized sorting algorithm. Note, also, that compar returns an int; the function pointed to should return -1 if the first argument is less than the second, 0 if they are equal, or 1 if the second is less than the first.
但真正让我们感兴趣的是qsort函数的参数compar,它是一个函数指针,它指向一个函数,这个函数含有两个空指针void*类型的形参,并且返回值是一个整数
For instance, to sort an array of numbers in ascending order, we could write code like this:
#include <stdlib.h>
int int_sorter( const void *first_arg, const void *second_arg )
{
int first = *(int*)first_arg;
int second = *(int*)second_arg;
if ( first < second )
{
return -1;
}
else if ( first == second )
{
return 0;
}
else
{
return 1;
}
}
int main()
{
int array[10];
int i;
/* fill array */
for ( i = 0; i < 10; ++i )
{
array[ i ] = 10 - i;
}
qsort( array, 10 , sizeof( int ), int_sorter );
for ( i = 0; i < 10; ++i )
{
printf ( "%d\n" ,array[ i ] );
}
}
Using Polymorphism and Virtual Functions Instead of Function Pointers (C++)
You can often avoid the need for explicit function pointers by using virtual functions. For instance, you could write a sorting routine that takes a pointer to a class that provides a virtual function called compare:
你应该使用虚函数,而不是使用显式的函数指针
class Sorter
{
public:
virtual int compare (const void *first, const void *second);
};
// cpp_qsort, a qsort using C++ features like virtual functions
void cpp_qsort(void *base, size_t nmemb, size_t size, Sorter *compar);
inside cpp_qsort, whenever a comparison is needed, compar->compare should be called. For classes that override this virtual function, the sort routine will get the new behavior of that function. For instance:
class AscendSorter : public Sorter
{
virtual int compare (const void*, const void*)
{
int first = *(int*)first_arg;
int second = *(int*)second_arg;
if ( first < second )
{
return -1;
}
else if ( first == second )
{
return 0;
}
else
{
return 1;
}
}
};
and then you could pass in a pointer to an instance of the AscendSorter to cpp_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 compiler makes the work easier for you. Using polymorphism can be an appropriate strategy (for instance, it's used by Java), but it does lead to the overhead of having to create an object rather than simply pass in a function pointer.
虚函数的本质就是使用函数指针(VTABLE和VPTR),因此当你使用虚函数时,你实质上是在使用函数指针,只不过这一些工作是由编译器来完成的,编译器极大地简化了你的工作。因此,使用多态不失为一种更为恰当的方式
Function Pointers Summary
Syntax
Declaring
Declare a function pointer as though you were declaring a function, except with a name like *foo instead of just foo:
声明一个函数指针就好像是声明一个函数,只不过是函数名是以“*foo”形式,而不是“foo”形式
void (*foo)(int);
Initializing
You can get the address of a function simply by naming it:
可以将一个函数的地址直接赋给函数指针
void foo();
func_pointer = foo;
or by prefixing the name of the function with an ampersand:
或者是在函数名前加上地址引用符&来获得函数地址,并将其赋给函数指针
void foo();
func_pointer = &foo;
Invoking
Invoke the function pointed to just as if you were calling a function.
使用函数指针可以以普通函数调用的方式去调用指针所指向的函数
func_pointer( arg1, arg2 );
or you may optionally dereference the function pointer before calling the function it points to:
或者你也可以通过接引用的方式去使用函数指针去调用指针所指向的函数
(*func_pointer)( arg1, arg2 );
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
函数指针的优点:
1. 通过传递函数地址,函数指针可以调用所指定的行为
2. 将函数指针作为函数形参,你可以写出更加灵活的函数和库函数,这样,可以通过函数指针来决定调用函数的行为
3. 函数指针的灵活性可以通过类的虚函数来实现
I am grateful to Alex Hoffer and Thomas Carriero for their comments on a draft of this article.
FROM:http://www.cprogramming.com/tutorial/function-pointers.html