【问题标题】:Trying to understand function pointers in C试图理解 C 中的函数指针
【发布时间】:2012-03-26 14:22:05
【问题描述】:

我正在尝试理解函数指针并且正在挣扎。我在 K&R 中看到了排序示例以及其他一些类似的示例。我的主要问题是计算机实际在做什么。我创建了一个非常简单的程序来尝试了解基础知识。请参阅以下内容:

#include <stdio.h>

int func0(int*,int*);
int func1(int*,int*);

int main(){

    int i = 1;
    myfunc(34,23,(int(*)(void*,void*))(i==1?func0:func1));//34 and 23 are arbitrary inputs



}

void myfunc(int x, int y, int(*somefunc)(void *, void *)){

    int *xx =&x;
    int *yy=&y;

    printf("%i",somefunc(xx,yy));


}

int func0(int *x, int *y){

    return (*x)*(*y);

}

int func1(int *x, int *y){

    return *x+*y;
} 

程序根据某个变量将两个数字相乘或相加(主函数中的 i - 可能应该是主函数中的参数)。 fun0 将两个整数相乘,而 func1 将它们相加。

我知道这个例子很简单,但是如何传递一个函数指针而不是在函数 myfunc 中放置一个条件? 即在 myfunc 中有以下内容:

if(i == 1)printf("%i",func0(xx,yy));

else printf("%i",func1(xx,yy));

如果我这样做,结果将是相同的,但不使用函数指针。

【问题讨论】:

    标签: c function pointers


    【解决方案1】:

    您对函数指针如何工作的理解很好。您没有看到的是软件系统将如何从使用函数指针中受益。当使用不知道其他组件的组件时,它们变得很重要。

    qsort() 就是一个很好的例子。 qsort 将让您对任何数组进行排序,并且实际上并不知道数组的组成部分。因此,如果您有一个结构数组,或者更可能是指向结构的指针,则必须提供一个可以比较结构的函数。

    struct foo {
      char * name;
      int magnitude;
      int something;
    };
    
    int cmp_foo(const void *_p1, const void *_p2)
    {
      p1 = (struct foo*)_p1;
      p2 = (struct foo*)_p2;
    
      return p1->magnitude - p2->magnitude;
    }
    
    struct foo ** foos;
    // init 10 foo structures...
    
    qsort(foos, 10, sizeof(foo *), cmp_foo);
    

    那么 foos 数组将根据幅度字段进行排序。

    如您所见,这允许您将 qsort 用于任何类型——您只需提供比较功能。

    函数指针的另一个常见用途是回调,例如在 GUI 编程中。如果您希望在单击按钮时调用函数,则应在设置按钮时提供指向 GUI 库的函数指针。

    【讨论】:

    • 您可能打算将 p1 和 p2 声明为 const struct foo*。哦,顺便说一句,你不能将指针传递给 qsort,它需要一个相邻分配的对象数组。如果你传递一个指针到指针,它会崩溃和烧毁。
    【解决方案2】:

    传递函数指针比在函数 myfunc 中放置条件更可取

    有时不可能在其中放置条件:例如,如果您正在编写排序算法,并且您不知道要提前排序什么,则根本无法放置条件;函数指针让您可以将一段计算“插入”到主算法中,而无需跳过任何环节。

    就机制的工作原理而言,想法很简单:所有编译后的代码都位于程序内存中,CPU 从某个地址开始执行它。有指令使 CPU 在地址之间跳转,记住当前地址并跳转,调用先前跳转的地址并返回,等等。当你调用一个函数时,CPU 需要知道的一件事是它在程序存储器中的地址。函数的名称代表该地址。您可以直接提供该地址,也可以将其分配给一个指针以进行间接访问。这类似于通过指针访问值,只是在这种情况下您间接访问代码,而不是访问数据。

    【讨论】:

    • 但您可以检查数组的第一个元素以查看它是字符串还是 int 或其他什么。如果它是一个字符串,那么做一个字典排序等等......
    • @JJG 字符串和数字以外的东西经常被排序,库的作者无法准备。例如,您可以对一组structs 进行排序,每个数组代表患者的医院记录。您可以编写一个函数,对名字进行排序,按姓氏解析并列关系,然后按出生日期解析剩余的关系。对于一般类型的作者来说,根本无法知道您将拥有这样的结构,更不用说以这种特定和不寻常的方式对其进行排序了。
    【解决方案3】:

    首先,您永远不能将函数指针类型转换为不同类型的函数指针。这是 C (C11 6.5.2.2) 中未定义的行为。

    在处理函数指针时,一个非常重要的建议是始终使用 typedef。

    因此,您的代码可以/应该重写为:

    typedef int (*func_t)(int*, int*);
    
    int func0(int*,int*);
    int func1(int*,int*);
    
    int main(){
    
        int i = 1;
        myfunc(34,23, (i==1?func0:func1)); //34 and 23 are arbitrary inputs
    }
    
    void myfunc(int x, int y, func_t func){
    

    要回答这个问题,当你不知道函数的性质时,你想使用函数指针作为参数。这在编写通用算法时很常见。

    以标准C函数bsearch()为例:

    void *bsearch (const void *key, 
                   const void *base,
                   size_t nmemb, 
                   size_t size,
                   int (*compar)(const void *, const void *));
                  );
    

    这是一个通用的二分搜索算法,搜索任何形式的一维数组,包含未知类型的数据,例如用户定义的类型。在这里,“比较”函数正在比较两个性质未知的对象是否相等,并返回一个数字来表示这一点。

    “如果键对象分别被认为小于、匹配或大于数组元素,则函数应返回小于、等于或大于零的整数。”

    函数由知道数据性质的调用者编写。在计算机科学中,这称为"function object" 或有时称为“函子”。在面向对象设计中经常遇到。

    一个例子(伪代码):

    typedef struct  // some user-defined type
    {
      int* ptr;
      int  x;
      int  y;
    } Something_t;
    
    int compare_Something_t (const void* p1, const void* p2)
    {
      const Something_t* s1 = (const Something_t*)p1;
      const Something_t* s2 = (const Something_t*)p2;
    
      return s1->y - s2->y;  // some user-defined comparison relevant to the object
    }
    
    ...
    
    Something_t  search_key = { ... };
    Something_t  array[] = { ... };
    Something_t* result; 
    
    result = bsearch(&search_key,
                     array,
                     sizeof(array) / sizeof(Something_t), // number of objects
                     sizeof(Something_t), // size of one object
                     compare_Something_t // function object
                    );
    

    【讨论】:

      猜你喜欢
      • 2013-10-08
      • 1970-01-01
      • 1970-01-01
      • 2011-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-28
      • 1970-01-01
      相关资源
      最近更新 更多