函数指针的意义是什么?

我很难看到函数指针的实用性。我想它可能在某些情况下有用(毕竟它们确实存在) ,但我想不出哪种情况下使用函数指针是更好或不可避免的。

你能给出一些函数指针(在 C 或 C + + 中)的良好使用的例子吗?

16744 次浏览

Examples:

  1. Custom sorting/searches
  2. Different patterns (like Strategy, Observer)
  3. Callbacks

Function pointers can be used in C to create an interface against which to program. Depending on the specific functionality that is needed at runtime, a different implementation can be assigned to the function pointer.

In C, the classic use is the qsort function, where the fourth parameter is pointer to a function to use to perform the ordering within the sort. In C++, one would tend to use functors (objects that look like functions) for this kind of thing.

The "classic" example for the usefulness of function pointers is the C library qsort() function, which implements a Quick Sort. In order to be universal for any and all data structures the user may come up with, it takes a couple of void pointers to sortable data and a pointer to a function that knows how to compare two elements of these data structures. This allows us to create our function of choice for the job, and in fact even allows for choosing the comparison function at run time, e.g. for sorting ascending or descending.

Most examples boil down to callbacks: You call a function f() passing the address of another function g(), and f() calls g() for some specific task. If you pass f() the address of h() instead, then f() will call back h() instead.

Basically, this is a way to parametrize a function: Some part of its behavior is not hard-coded into f(), but into the callback function. Callers can make f() behave differently by passing different callback functions. A classic is qsort() from the C standard library that takes its sorting criterion as a pointer to a comparison function.

In C++, this is often done using function objects (also called functors). These are objects that overload the function call operator, so you can call them as if they were a function. Example:

class functor {
public:
void operator()(int i) {std::cout << "the answer is: " << i << '\n';}
};


functor f;
f(42);

The idea behind this is that, unlike a function pointer, a function object can carry not only an algorithm, but also data:

class functor {
public:
functor(const std::string& prompt) : prompt_(prompt) {}
void operator()(int i) {std::cout << prompt_ << i << '\n';}
private:
std::string prompt_;
};


functor f("the answer is: ");
f(42);

Another advantage is that it is sometimes easier to inline calls to function objects than calls through function pointers. This is a reason why sorting in C++ is sometimes faster than sorting in C.

Agree with all of the above, plus.... When you load a dll dynamically at runtime you'll need function pointers to call the functions.

I am going to go against the current here.

In C, function pointers are the only way to implement customization, because there is no OO.

In C++, you can use either function pointers or functors (function objects) for the same result.

The functors have a number of advantages over raw function pointers, due to their object nature, notably:

  • They may present several overloads of the operator()
  • They can have state / reference to existing variables
  • They can be built on the spot (lambda and bind)

I personally prefer functors to function pointers (despite the boilerplate code), mostly because the syntax for function pointers can easily get hairy (from the Function Pointer Tutorial):

typedef float(*pt2Func)(float, float);
// defines a symbol pt2Func, pointer to a (float, float) -> float function


typedef int (TMyClass::*pt2Member)(float, char, char);
// defines a symbol pt2Member, pointer to a (float, char, char) -> int function
// belonging to the class TMyClass

The only time I have ever seen function pointers used where functors could not was in Boost.Spirit. They have utterly abused the syntax to pass an arbitrary number of parameters as a single template parameter.

 typedef SpecialClass<float(float,float)> class_type;

But since variadic templates and lambdas are around the corner, I am not sure we will use function pointers in pure C++ code for long now.

Well, I generally use them (professionally) in jump tables (see also this StackOverflow question).

Jump tables are commonly (but not exclusively) used in finite state machines to make them data driven. Instead of nested switch/case

  switch (state)
case A:
switch (event):
case e1: ....
case e2: ....
case B:
switch (event):
case e3: ....
case e1: ....

you can make a 2d array of function pointers and just call handleEvent[state][event]

I used function pointers recently to create an abstraction layer.

I have a program written in pure C that runs on embedded systems. It supports multiple hardware variants. Depending on the hardware I am running on, it needs to call different versions of some functions.

At initialization time, the program figures out what hardware it is running on and populates the function pointers. All of the higher-level routines in the program just call the functions referenced by pointers. I can add support for new hardware variants without touching the higher-level routines.

I used to use switch/case statements to select the proper function versions, but this became impractical as the program grew to support more and more hardware variants. I had to add case statements all over the place.

I also tried intermediate function layers to figure out which function to use, but they didn't help much. I still had to update case statements in multiple places whenever we added a new variant. With the function pointers, I only have to change the initialization function.

I use function pointers extensively, for emulating microprocessors that have 1-byte opcodes. An array of 256 function pointers is the natural way to implement this.

My main use of them has been CALLBACKS: when you need to save information about a function to call later.

Say you're writing Bomberman. 5 seconds after the person drops the bomb, it should explode (call the explode() function).

Now there's 2 ways to do it. One way is by "probing" all bombs on the screen to see if they're ready to explode in the main loop.

foreach bomb in game
if bomb.boomtime()
bomb.explode()

Another way is to attach a callback to your clock system. When a bomb is planted, you add a callback to make it call bomb.explode() when the time is right.

// user placed a bomb
Bomb* bomb = new Bomb()
make callback( function=bomb.explode, time=5 seconds ) ;


// IN the main loop:
foreach callback in callbacks
if callback.timeToRun
callback.function()

Here callback.function() can be any function, because it is a function pointer.

One use of function pointer could be where we may not want to modify the code where the function is getting called (meaning thereby the call might be conditional and under different conditions, we need to do different sort of processing). Here the function pointers are very handy, since we do not need to modify the code at the the place where the function is getting called. We simply call the function using the function pointer with appropriate arguments. The function pointer can be made to point to different functions conditionally. (This can be done somewhere during initialization phase). Moreover the above model is very helpful, if we are not in position to modify the code where it is getting called (suppose it's a library API we can't modify). The API uses a function pointer for calling the appropriate user defined function.

Like Rich said above, it is very usual for functions pointers in Windows to reference some address that stores function.

When you programming in C language on Windows platform you basically load some DLL file in primary memory(using LoadLibrary) and to use the functions stored in DLL you need to create functions pointers and point to these address (using GetProcAddress).

References:

For OO languages, to perform polymorphic calls behind the scenes (this is also valid for C up to some point I guess).

Moreover, they're very useful to inject different behaviour to another function (foo) at runtime. That makes function foo higher-order function. Besides it's flexibility, that makes the foo code more readable since it let's you pull that extra logic of "if-else" out of it.

It enables many other useful things in Python like generators, closures etc.

A different perspective, in addition to other good answers here:

In C, you only use function pointers, not (directly) functions.

I mean, you write functions, but you cant manipulate functions. There's no run-time representation of a function as such which you are able to use. You can't even call "a function". When you write:

my_function(my_arg);

what you're actually saying is "perform a call to the my_function pointer with the specified argument". You're making a call via a function pointer. This decay to function pointer means that the following commands are equivalent to the previous function call:

(&my_function)(my_arg);
(*my_function)(my_arg);
(**my_function)(my_arg);
(&**my_function)(my_arg);
(***my_function)(my_arg);

and so on (thanks @LuuVinhPhuc).

So, you're already using function pointers as values. Obviously you would want to have variables for those values - and here is where all the uses other metion come in: Polymorphism/customization (like in qsort), callbacks, jump tables etc.

In C++ things are a bit more complicated, since we have lambdas, and objects with operator(), and even an std::function class, but the principle is still mostly the same.

Use of function pointer

To call function dynamically based on user input. By creating a map of string and function pointer in this case.

#include<iostream>
#include<map>
using namespace std;
//typedef  map<string, int (*)(int x, int y) > funMap;
#define funMap map<string, int (*)(int, int)>
funMap objFunMap;


int Add(int x, int y)
{
return x+y;
}
int Sub(int x, int y)
{
return x-y;
}
int Multi(int x, int y)
{
return x*y;
}
void initializeFunc()
{
objFunMap["Add"]=Add;
objFunMap["Sub"]=Sub;
objFunMap["Multi"]=Multi;
}
int main()
{
initializeFunc();


while(1)
{
string func;
cout<<"Enter your choice( 1. Add 2. Sub 3. Multi) : ";
int no, a, b;
cin>>no;


if(no==1)
func = "Add";
else if(no==2)
func = "Sub";
else if(no==3)
func = "Multi";
else
break;


cout<<"\nEnter 2 no :";
cin>>a>>b;


//function is called using function pointer based on user input
//If user input is 2, and a=10, b=3 then below line will expand as "objFuncMap["Sub"](10, 3)"
int ret = objFunMap[func](a, b);
cout<<ret<<endl;
}
return 0;
}

This way we have used function pointer in our actual company code. You may write 'n' number of function and call them using this method.

OUTPUT:

Enter your choice( 1. Add 2. Sub 3. Multi) : 1
Enter 2 no :2 4
6
Enter your choice( 1. Add 2. Sub 3. Multi) : 2
Enter 2 no : 10 3
7
Enter your choice( 1. Add 2. Sub 3. Multi) : 3
Enter 2 no : 3 6
18

I'll try to give a somewhat comprehensive list here:

  • Callbacks: Customize some (library) functionality with user supplied code. Prime example is qsort(), but also useful to handle events (like a button calling a callback when it's clicked), or necessary to start a thread (pthread_create()).

  • Polymorphism: The vtable in a C++ class is nothing but a table of function pointers. And a C program may also choose to provide a vtable for some of its objects:

    struct Base;
    struct Base_vtable {
    void (*destruct)(struct Base* me);
    };
    struct Base {
    struct Base_vtable* vtable;
    };
    
    
    struct Derived;
    struct Derived_vtable {
    struct Base_vtable;
    void (*frobnicate)(struct Derived* me);
    };
    struct Derived {
    struct Base;
    int bar, baz;
    }
    

    The constructor of Derived would then set its vtable member variable to a global object with the derived's class's implementations of destruct and frobnicate, and code that needed to destruct a struct Base* would simply call base->vtable->destruct(base), which would call the correct version of the destructor, independent of which derived class base actually points to.

    Without function pointers, polymorphism would need to be coded out with an army of switch constructs like

    switch(me->type) {
    case TYPE_BASE: base_implementation(); break;
    case TYPE_DERIVED1: derived1_implementation(); break;
    case TYPE_DERIVED2: derived2_implementation(); break;
    case TYPE_DERIVED3: derived3_implementation(); break;
    }
    

    This gets rather unwieldy rather quickly.

  • Dynamically loaded code: When a program loads a module into memory and tries to call into its code, it must go through a function pointer.

All the uses of function pointers that I've seen fall squarely into one of these three broad classes.

They enhance re-use and modularization of code thus making code more maintainable, readable and less prone to errors.

With function pointers:

Note how we have an iterator method that gets passed to it a function pointer. This function pointer tells us what we should do with each elements in the list.

#include <iostream>
#include <vector>


int square(int x) {
return x * x;
}


int root(int x) {
return sqrt(x);
}


int negative(int x) {
return -x;
}


std::vector<int> listIterator(std::vector<int> list, int (*itemOperation)(int)) {
for (int i = 0; i < list.size(); i++) {
list[i] = itemOperation(list[i]);
}
return list;
}


int main() {
std::vector<int> list = { 9, 16, 4, 25 };
for (int i : listIterator(list, square)) {
std::cout << i << ' ';
}
std::cout << std::endl;
for (int i : listIterator(list, root)) {
std::cout << i << ' ';
}
std::cout << std::endl;
for (int i : listIterator(list, negative)) {
std::cout << i << ' ';
}
return 0;
}

Without function pointers:

Without function pointers, you would need to include an iterator in each of the square, root and negative methods.