C 使用函数指针作为方法参数

C 使用函数指针作为方法参数,c,function-pointers,C,Function Pointers,我试图使用函数指针来提高代码的效率。然而,我对这个概念还不熟悉,在将它用作另一个函数中的参数时遇到了麻烦 void print(node_t *node, void *param) { FILE *f = param; // no cast needed ... 这会产生一个错误“从不兼容的指针类型初始化”。 有人能告诉我我做错了什么以及我应该做什么吗?当你这样做时: traverse(node, h, print(node, h)); 实际上,您正在尝试调用打印,而不是向

我试图使用函数指针来提高代码的效率。然而,我对这个概念还不熟悉,在将它用作另一个函数中的参数时遇到了麻烦

void print(node_t *node, void *param)
{
    FILE *f = param;  // no cast needed
    ...
这会产生一个错误“从不兼容的指针类型初始化”。 有人能告诉我我做错了什么以及我应该做什么吗?

当你这样做时:

traverse(node, h, print(node, h));
实际上,您正在尝试调用打印,而不是向它传递指针。因此,您要传递给
travel
的实际上是
print
的返回值,而不是指向它的指针

正确的调用如下所示:

traverse(node, h, print);
然后在
遍历
的内部调用回调:

void traverse(node_t *node, void *param, func_t func)
{
    ...
    func(node,param);
    ...
}
然而,仍然存在一个问题。函数
print
的类型与函数指针
func\u t
不兼容。前者将
FILE*
作为其第一个参数,而后者将
void*
作为其第二个参数。与
void*
之间的隐式转换仅在它是转换的源或目标时有效。它不适用于函数参数

假设您需要回调来处理各种不同的类型,您可以将
print
函数更改为接受
void*
并转换函数内部的参数

void print(node_t *node, void *param)
{
    FILE *f = param;  // no cast needed
    ...

首先:仅仅因为可以使用
void*
代替任何其他对象指针,这并不意味着指向接受
void*
的函数的函数指针与接受
文件*
的另一个函数兼容

因此,您必须更改函数指针类型以使用
FILE*
,或者必须更改打印函数以使用
void*

但是,编译器错误的原因是您在此处调用实际函数
print
traverse(node,h,print(node,h)),而不是将函数指针传递给它。由于此函数返回
void
,编译器会说“嘿,我不能将void参数传递给此函数,因为它需要函数指针”。只需将此更改为:

traverse(node, h, print);

然后遍历<代码>将通过传递的函数指针调用函数。

< p>对于它的价值,也许你使用的是错误的编程语言:)你想要写的东西是C++,它是:

traverse(node, [node,h]{ print(node, h); });
然后,traverse将是一个模板函数,因此您不必仅传递一种函数,而是可以传递任何函子(可调用对象,例如实现调用运算符的类):

为了完整性起见,以下是它在C++中的外观:

struct Node {
  std::vector<Node> children;
  std::string name;
};

template <typename Obs>
void ctraverse(const Node &node, F fun, int level = 0) {
  fun(node, level);
  ++level;
  for (auto &n : node.children) {
    ctraverse(n, fun);
  }
}

void test(const Node &node, std::ostream &out) {
  traverse(node, [&](auto &node, int level) { 
    out << setw(level) << setfill(' ') << " " << node.name << '\n';
  });
}
struct节点{
性病媒儿童;
std::字符串名;
};
模板
void ctraverse(常量节点和节点,F fun,int level=0){
乐趣(节点,级别);
++水平;
用于(自动编号:node.children(&n){
ctraverse(n,fun);
}
}
无效测试(常数节点和节点,标准::ostream和out){
遍历(节点,[&](自动&节点,整数级){

我不应该为print传递参数?这是如何工作的?print如何知道要使用哪些参数?将参数传递给在第三个参数中传递的函数是
遍历
的任务。嘿,几乎相同的答案。对于FGITW,我给你一个+1:)谢谢你的回答。我照你说的做了,效果很好。不过,我只是在玩,我注意到这也是对的:func_t fun=(func_t)print;traverse(node,param,fun);为什么这也是正确的?@gazillionTickets假设您没有修改
打印
,这是不对的。您通过不兼容的指针类型调用函数来调用。您刚刚得到了“幸运”它是有效的。所以基本上,虽然我的代码可以工作,但它容易受到bug的攻击?@gazillionTickets它可能会工作,而做一些不相关的事情可能会突然导致它不工作。谢谢你的回答。我照你说的做了,它工作了。但是,我只是在玩,我注意到这也是正确的:func_t fun=(func_t)print;traverse(node,param,fun);为什么这也是正确的?@gazillionTickets它不是“正确的”,它是未定义的行为。未定义行为的一种形式是:代码工作正常(这次)。
struct Node {
  std::vector<Node> children;
  std::string name;
};

template <typename Obs>
void ctraverse(const Node &node, F fun, int level = 0) {
  fun(node, level);
  ++level;
  for (auto &n : node.children) {
    ctraverse(n, fun);
  }
}

void test(const Node &node, std::ostream &out) {
  traverse(node, [&](auto &node, int level) { 
    out << setw(level) << setfill(' ') << " " << node.name << '\n';
  });
}