什么是函数指针,我将如何使用它们?

时间:2009-11-18 19:46:21

标签: c pointers function-pointers

我知道我可以使用函数指针。

有人可以解释为什么会使用它们,以及如何使用它们?简短的示例代码对我很有帮助。

9 个答案:

答案 0 :(得分:22)

一个简单的例子是这样的:根据您的业务逻辑,您有一系列操作(函数)。您有一个散列函数可以将输入问题减少到其中一个业务逻辑函数。一个干净的代码将有一个函数指针数组,你的程序将从输入推断出该数组的索引并调用它。

以下是示例代码:

typedef void (*fn)(void) FNTYPE;
FNTYPE fn_arr[5];

fn_arr[0] = fun1; // fun1 is previously defined
fn_arr[1] = fun2;
...

void callMyFun(string inp) {
    int idx = decideWhichFun(inp); // returns an int between 0 and 4
    fn_arr[idx]();
}

但当然,回调是最常见的用法。示例代码如下:

void doLengthyOperation(string inp, void (*callback)(string status)) {
  // do the lengthy task
  callback("finished");
}

void fnAfterLengthyTask(string status) {
    cout << status << endl;
}

int main() {
    doLengthyOperation(someinput, fnAfterLengthyTask);
}

答案 1 :(得分:13)

一个很常见的用例是回调函数。例如,如果从DB加载某些内容,则可以实现加载功能,以便将进度报告给回调函数。这可以通过函数指针完成。

答案 2 :(得分:5)

回调。我对一大块代码进行异步调用,并希望它在完成时让我知道,我可以发送一个函数指针,一旦完成就调用它。

答案 3 :(得分:3)

当您需要提供回调方法时,可以使用函数指针。一个典型的例子是注册信号处理程序 - 当程序获得SIGTERM(Ctrl-C)时将调用该函数

这是另一个例子:

// The four arithmetic operations ... one of these functions is selected
// at runtime with a switch or a function pointer
float Plus    (float a, float b) { return a+b; }
float Minus   (float a, float b) { return a-b; }
float Multiply(float a, float b) { return a*b; }
float Divide  (float a, float b) { return a/b; }

// Solution with a switch-statement - <opCode> specifies which operation to execute
void Switch(float a, float b, char opCode)
{
   float result;

   // execute operation
   switch(opCode)
   {
      case '+' : result = Plus     (a, b); break;
      case '-' : result = Minus    (a, b); break;
      case '*' : result = Multiply (a, b); break;
      case '/' : result = Divide   (a, b); break;
   }

   cout << "Switch: 2+5=" << result << endl;         // display result
}  

// Solution with a function pointer - <pt2Func> is a function pointer and points to
// a function which takes two floats and returns a float. The function pointer
// "specifies" which operation shall be executed.
void Switch_With_Function_Pointer(float a, float b, float (*pt2Func)(float, float))
{
   float result = pt2Func(a, b);    // call using function pointer

   cout << "Switch replaced by function pointer: 2-5=";  // display result
   cout << result << endl;
}

您可以在http://www.newty.de/fpt/index.html

了解有关函数指针的更多信息

如果您更熟悉面向对象语言,可以将其视为C实现strategy design pattern的方式。

答案 4 :(得分:3)

我很惊讶没有人提到“国家机器”。函数指针是为解析等任务实现状态机的一种非常常见的方法。例如,请参阅:link

答案 5 :(得分:1)

让我们为C做一个类似map的函数。

void apply(int *arr, size_t len, int (*func)(int))
{
    for(size_t i = 0; i < len; i++)
        arr[i] = func(arr[i]);
}

这样,我们可以转换一个对整数有效的函数来处理整数数组。我们也可以做类似的版本:

void apply_enumerated(int *arr, size_t len, int (*func)(size_t, int))
{
    for(size_t i = 0; i < len; i++)
        arr[i] = func(i, arr[i]);
}

这可以做同样的事情,但允许我们的函数知道它所在的元素。我们可以使用它,例如:

int cube(int i) { return i * i * i }

void print_array(int *array, size_t len, char *sep)
{
    if(sep == NULL) sep = ", ";
    printf("%d", *array);
    for(size_t i = 1; i < len; i++) printf("%s%d", sep, array[i])
}

#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))

int main(void)
{
    int array[5] = { 1, 2, 3, 4, 5 };
    print_array(array, ARRAY_SIZE(array), NULL);
    apply(array, ARRAY_SIZE(array), cube);
    print_array(array, ARRAY_SIZE(array), NULL);
    return 0;
}

该代码将打印出来:

1, 2, 3, 4, 5
1, 8, 27, 64, 125

对于我们的枚举示例:

int mult(size_t i, int j) { return i * j }

// print_array and ARRAY_SIZE as before

int main(void)
{
    int array[5] = { 1, 2, 3, 4, 5 };
    print_array(array, ARRAY_SIZE(array), NULL);
    apply_enumerated(array, ARRAY_SIZE(array), mult);
    print_array(array, ARRAY_SIZE(array), NULL);
    return 0;
}

打印:

1, 2, 3, 4, 5
0, 2, 6, 12, 20

作为一个更真实的例子,字符串库可以有一个函数,它将对单个字符进行操作的函数应用于字符串中的所有字符。这些函数的一个示例是toupper()中的标准库tolower()ctype.h函数 - 我们可以使用此string_apply()函数轻松创建string_toupper()函数。

答案 6 :(得分:1)

一个非常好且易于理解的教程: http://www.newty.de/fpt/index.html

希望这有帮助。

答案 7 :(得分:0)

指针的另一种用法是迭代列表或数组。

答案 8 :(得分:0)

对于代码,请查看qrdl对state machines tutorials的回复。

我使用了他的方法的变体来实现我在板上的LCD显示器的菜单。非常有用,使编码更清晰,更容易阅读。使用函数指针可以非常容易地扩展菜单的数量,名称和顺序,并且隐藏所有那些细节来自更高级别的调用函数,这些函数只是看到了“嘿,我在这里写入LCD”。