【问题标题】:What are function pointers used for, and how would I use them?函数指针的用途是什么,我将如何使用它们?
【发布时间】:2010-12-18 00:44:44
【问题描述】:

我知道我可以将指针用于函数。

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

【问题讨论】:

  • 查看标准库函数qsort() 看看是否对您有帮助。
  • 它们也经常在操作系统的底层使用。就像我使用很多函数指针创建了一个带有驱动程序框架的操作系统一样。它们非常方便,因为它就像virtual/abstract 方法,除了因为你必须控制它和管理它..
  • @Chris Lutz:+1 你的评论可能是一个很好的答案,如果它被表述为一个答案并解释了它的一个(qsort)基础(函数指针),我会给予支持

标签: c pointers function-pointers


【解决方案1】:

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

这是一个示例代码:

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);
}

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 我的最爱。但不仅仅是报告进度——实际上是传回所有记录!
    • 这确实是一个非常常见的用例。
    【解决方案3】:

    回调。我对一段代码进行了异步调用,并希望它在完成时通知我,我可以向它发送一个函数指针,以便在完成后调用。

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        当您需要提供回调方法时,您可以使用函数指针。经典示例之一是注册信号处理程序 - 当您的程序获得 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。

        【讨论】:

        • 如果 C 语言有 cout &lt;&lt; endl 会 +1(而且你的 C++ 代码需要 using namespace std; 才能工作)
        • opcode 在你的Switch_With_Function_Pointer() 函数中在哪里提到?
        • @Joy - 在switch() 声明中。
        【解决方案6】:

        让我们为 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
        

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

        【讨论】:

          【解决方案7】:

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

          希望这会有所帮助。

          【讨论】:

            【解决方案8】:

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

            【讨论】:

            【解决方案9】:

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

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2010-11-01
              • 2016-06-01
              • 1970-01-01
              • 2013-04-30
              • 2016-10-24
              • 2010-12-04
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多