9

假设我正在创建一个国际象棋程序。我有一个功能

void foreachMove( void (*action)(chess_move*), chess_game* game); 

这将在每次有效移动时调用函数指针操作。这一切都很好,但是如果我需要向动作函数传递更多参数怎么办?例如:

chess_move getNextMove(chess_game* game, int depth){
  //for each valid move, determine how good the move is
  foreachMove(moveHandler, game);
}

void moveHandler(chess_move* move){
  //uh oh, now I need the variables "game" and "depth" from the above function
}

重新定义函数指针并不是最优解。foreachMove 函数用途广泛,代码中的许多不同位置都引用了它。这些引用中的每一个都必须更新它们的函数以包含它们不需要的参数是没有意义的。

如何将额外的参数传递给我通过指针调用的函数?

4

9 回答 9

13

啊,要是只有 C 支持闭包就好了……

安东尼奥是对的;如果您需要传递额外的参数,则需要重新定义函数指针以接受额外的参数。如果您不确切知道需要哪些参数,那么您至少有三个选择:

  1. 让原型中的最后一个参数为 void*。这使您可以灵活地传递您需要的任何其他内容,但它绝对不是类型安全的。
  2. 使用可变参数 (...)。鉴于我缺乏 C 中可变参数的经验,我不确定您是否可以将其与函数指针一起使用,但这比第一个解决方案提供了更大的灵活性,尽管仍然缺乏类型安全性。
  3. 升级到 C++ 并使用函数对象
于 2008-08-14T17:19:28.183 回答
7

您可能需要重新定义函数指针以获取其他参数。

void foreachMove( void (*action)(chess_move*, int), chess_game* game )
于 2008-08-14T16:58:47.600 回答
4

如果您愿意使用一些 C++,您可以使用“函数对象”:

struct MoveHandler {
    chess_game *game;
    int depth;

    MoveHandler(chess_game *g, int d): game(g), depth(d) {}

    void operator () (chess_move*) {
         // now you can use the game and the depth
    }
};

并将您的foreachMove变成模板:

template <typename T>
void foreachMove(T action, chess_game* game);

你可以这样称呼它:

chess_move getNextMove(chess_game* game, int depth){
    //for each valid move, determine how good the move is
    foreachMove(MoveHandler(game, depth), game);
}

但它不会破坏您对MoveHandler.

于 2008-08-14T17:32:18.967 回答
2

如果我没看错,我建议让你的函数将指向结构的指针作为参数。然后,您的结构可以在需要它们时具有“游戏”和“深度”,并在不需要它们时将它们设置为 0 或 Null。

该功能发生了什么?你有一个条件说,

if (depth > -1) //some default
  {
  //do something
  }

该功能是否总是需要“游戏”和“深度”?然后,它们应该始终是参数,并且可以进入您的原型。

您是否表示该功能仅有时需要“游戏”和“深度”?好吧,也许可以制作两个功能,并在需要时使用每个功能。

但是,将结构作为论据可能是最简单的事情。

于 2008-08-14T17:24:57.423 回答
1

我建议使用一个 void* 数组,最后一个条目总是无效的。说你需要 3 个参数,你可以这样做:

void MoveHandler (void** DataArray)
{
    // data1 is always chess_move
    chess_move data1 = DataArray[0]? (*(chess_move*)DataArray[0]) : NULL; 
    // data2 is always float
    float data1 = DataArray[1]? (*(float*)DataArray[1]) : NULL; 
    // data3 is always char
    char data1 = DataArray[2]? (*(char*)DataArray[2]) : NULL; 
    //etc
}

void foreachMove( void (*action)(void**), chess_game* game);

接着

chess_move getNextMove(chess_game* game, int depth){
    //for each valid move, determine how good the move is
    void* data[4];
    data[0] = &chess_move;
    float f1;
    char c1;
    data[1] = &f1;
    data[2] = &c1;
    data[3] = NULL;
    foreachMove(moveHandler, game);
}

如果所有参数都是相同的类型,那么您可以避免使用 void* 数组,而只需发送您需要的任何类型的以 NULL 结尾的数组。

于 2008-08-14T18:15:26.447 回答
0

+1 给安东尼奥。您需要更改函数指针声明以接受其他参数。

另外,请不要开始传递 void 指针或(尤其是)void 指针数组。那只是自找麻烦。如果您开始传递 void 指针,您还必须传递某种消息来指示指针类型是什么(或类型是什么)。这种技术很少适用。

如果您的参数始终相同,只需将它们添加到您的函数指针参数中(或者可能将它们打包到一个结构中并在有很多参数时将其用作参数)。如果您的参数发生变化,请考虑对多个调用场景使用多个函数指针,而不是传递 void 指针。

于 2008-08-14T18:39:46.617 回答
0

如果您的参数发生变化,我会更改函数指针声明以使用“...”技术来设置可变数量的参数。它可以节省您的可读性,并且还必须对要传递给函数的每个参数进行更改。它肯定比传递 void 安全得多。

http://publications.gbdirect.co.uk/c_book/chapter9/stdarg.html

仅供参考,关于链接中的示例代码:有些地方有“n args”,有些地方是“n_args”,带有下划线。他们都应该有下划线。我认为语法看起来有点滑稽,直到我意识到他们在某些地方删除了下划线。

于 2008-08-14T20:22:54.740 回答
0

Use a typedef for the function pointer. See my answer for this question

于 2008-09-16T14:57:42.703 回答
0

另一种选择是修改chess_move结构而不是函数原型。该结构大概只在一个地方定义过。将成员添加到结构中,并在使用它的任何调用之前用适当的数据填充结构。

于 2013-04-08T05:08:22.313 回答