在C函数指针中传递更多参数
假设我正在创建一个国际象棋程序。我有一个功能
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函数具有多种功能,并且代码中的许多不同地方都引用了该函数。这些引用中的每个引用都不必更新其功能以包含它们不需要的参数,这是没有意义的。
如何通过指针将额外的参数传递给正在调用的函数?
解决方案
回答
我们可能需要重新定义函数指针以采用其他参数。
void foreachMove( void (*action)(chess_move*, int), chess_game* game )
回答
啊,如果只有C支持闭包...
安东尼奥是对的。如果需要传递其他参数,则需要重新定义函数指针以接受其他参数。如果我们不确定所需的参数,那么至少有三个选择:
- 原型中的最后一个参数为void *。这使我们可以灵活地传递我们需要的其他任何内容,但这绝对不是类型安全的。
- 使用可变参数(...)。考虑到我缺乏C语言中可变参数的经验,我不确定是否可以将其与函数指针一起使用,但这比第一种解决方案具有更大的灵活性,尽管仍然缺乏类型安全性。
- 升级到C ++并使用函数对象。
回答
如果我没看错,我的建议是使函数将指向结构的指针作为参数。然后,结构在需要它们时可以具有"游戏"和"深度",并且在不需要它们时将它们设置为0或者Null。
该功能发生了什么?我们是否有条件说:
if (depth > -1) //some default { //do something }
该功能是否总是需要"游戏"和"深度"?然后,它们应该始终是参数,并且可以放入原型中。
我们是否表示该功能有时仅需要"游戏"和"深度"?好吧,也许要做两个功能,并在需要时使用每个功能。
但是,将结构作为参数可能是最简单的事情。
回答
如果我们愿意使用某些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
的其他使用。
回答
我建议使用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终止数组。
回答
+1安东尼奥。我们需要更改函数指针声明以接受其他参数。
另外,请不要开始传递空指针或者(尤其是)空指针数组。那只是自找麻烦。如果我们开始传递void指针,则还必须传递某种消息以指示指针类型是(或者多个)指针类型。这种技术很少适合。
如果参数始终相同,则只需将它们添加到函数指针参数中即可(或者,如果有很多参数,则可以将它们打包到一个struct中并用作参数)。如果参数更改,则考虑在多个调用方案中使用多个函数指针,而不要传递空指针。
回答
如果参数更改,我将更改函数指针声明以使用" ..."技术来设置可变数量的参数。它可以节省可读性,并且还必须为要传递给该函数的每个参数进行更改。绝对比绕过虚空要安全得多。
http://publications.gbdirect.co.uk/c_book/chapter9/stdarg.html
仅供参考,关于链接中的示例代码:某些地方有n个args,其他地方是n_args,带下划线。他们都应该有下划线。我以为语法看起来有些可笑,直到我意识到他们在某些地方删除了下划线。
回答
使用typedef作为函数指针。看到我对这个问题的回答