在C语言函数指针中传递更多参数

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函数非常通用,代码中的许多不同位置都引用它。每个引用都需要更新其函数以包含它们不需要的参数是没有意义的。
我如何通过指针调用函数并传递额外的参数?
9个回答

13

啊,如果C支持闭包就好了...

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

  1. 在你的原型中将最后一个参数设为void*。这样可以灵活地传递任何你需要的东西,但它肯定不是类型安全的。
  2. 使用可变参数(...)。鉴于我在C语言中缺乏可变参数的经验,我不确定你是否可以将其与函数指针一起使用,但这比第一种解决方案更具灵活性,尽管仍然缺乏类型安全性。
  3. 升级到C++并使用function objects

7

您可能需要重新定义函数指针以接受额外的参数。

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

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 的其他使用。


2
如果我理解得没错的话,我建议你将函数作为参数传递一个指向结构体的指针。然后,在需要时,您的结构体可以拥有“game”和“depth”,而当您不需要它们时,只需将它们设置为0或Null即可。
那个函数里发生了什么?您是否有一个条件语句,
if (depth > -1) //some default
  {
  //do something
  }

这个函数是否总是需要“game”和“depth”?如果是的话,它们应该始终作为参数,并且可以放在您的原型中。

如果这个函数只有时候需要“game”和“depth”,那么也许可以创建两个函数,在需要时使用每个函数。

但是,将一个结构作为参数可能是最简单的方法。


1
我建议使用一个void*数组,最后一个条目始终为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结尾的数组即可。


0
另一种选择是修改chess_move结构而不是函数原型。这个结构可能已经在一个地方定义过了。向结构中添加成员,并在使用它的任何调用之前使用适当的数据填充结构。

0

+1 给 Antonio。你需要更改函数指针声明以接受其他参数。

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

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


0
如果您的参数发生变化,我会更改函数指针声明,使用“…”技术设置可变数量的参数。这样可以提高可读性,而且不需要为要传递给函数的每个参数进行更改。它绝对比传递void更安全。

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

关于链接中的示例代码,只是提醒一下:有些地方使用“n args”,而其他地方则使用带下划线的“n_args”。它们应该全部都使用下划线。我觉得语法看起来有点奇怪,直到我意识到它们在某些地方省略了下划线。


0

使用typedef来定义函数指针。请参考我的答案以获取关于这个问题的更多信息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接