假设我正在创建一个国际象棋程序。 我有一个功能
1
| void foreachMove( void (*action)(chess_move*), chess_game* game); |
这将在每个有效的移动中调用函数指针动作。 这一切都很好,但是如果我需要将更多参数传递给动作函数呢? 例如:
1 2 3 4 5 6 7 8
| 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函数是通用的,代码中的许多不同的地方都引用它。 对于每个引用都必须更新它们的函数以包含它们不需要的参数是没有意义的。
如何将额外的参数传递给我通过指针调用的函数?
啊,如果只有C支持的闭包......
安东尼奥是对的;如果需要传递额外的参数,则需要重新定义函数指针以接受其他参数。如果你不确切知道你需要什么参数,那么你至少有三个选择:
让原型中的最后一个参数成为无效*。这使您可以灵活地传递您需要的任何其他内容,但它绝对不是类型安全的。
使用可变参数(...)。鉴于我缺乏C语言中可变参数的经验,我不确定你是否可以将它与函数指针一起使用,但这比第一种解决方案更灵活,尽管仍然缺乏类型安全性。
升级到C ++并使用函数对象。
您可能需要重新定义函数指针以获取其他参数。
1
| void foreachMove( void (*action)(chess_move*, int), chess_game* game ) |
如果您愿意使用某些C ++,可以使用"函数对象":
1 2 3 4 5 6 7 8 9 10
| 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转换为模板:
1 2
| template <typename T>
void foreachMove(T action, chess_game* game); |
你可以这样称呼它:
1 2 3 4
| chess_move getNextMove(chess_game* game, int depth){
//for each valid move, determine how good the move is
foreachMove(MoveHandler(game, depth), game);
} |
但它不会破坏你对MoveHandler的其他用途。
如果我正在读这个,那么我建议的是让你的函数将一个结构的指针作为一个参数。然后,你的结构在需要它时可以有"游戏"和"深度",只要你不需要它们就把它们设置为0或Null。
这个功能发生了什么?你有条件说,
1 2 3 4
| if (depth > -1) //some default
{
//do something
} |
该功能总是需要"游戏"和"深度"吗?然后,它们应该始终是参数,并且可以进入您的原型。
你是否表示该功能有时只需要"游戏"和"深度"?好吧,也许做两个功能,并在需要时使用每个功能。
但是,以结构作为参数可能是最简单的事情。
我建议使用void *数组,最后一个条目总是无效。
假设您需要3个参数,您可以这样做:
1 2 3 4 5 6 7 8 9 10 11 12
| 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); |
接着
1 2 3 4 5 6 7 8 9 10 11
| 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终止数组。
另一种选择是修改chess_move结构而不是函数原型。该结构可能只在一个地方定义。将成员添加到结构中,并在使用它之前使用适当的数据填充结构。
对函数指针使用typedef。请参阅我对此问题的回答
如果您的参数发生变化,我会更改函数指针声明以使用"..."技术来设置可变数量的参数。它可以节省您的可读性,还必须对要传递给函数的每个参数进行更改。它绝对比传递虚空更安全。
http://publications.gbdirect.co.uk/c_book/chapter9/stdarg.html
仅仅是一个FYI,关于链接中的示例代码:一些地方他们有"n args"而另一些地方是带有下划线的"n_args"。他们都应该有下划线。我认为语法看起来有点滑稽,直到我意识到他们在某些地方放下了下划线。
+1来安东尼奥。您需要更改函数指针声明以接受其他参数。
另外,请不要开始传递void指针或(特别是)void指针数组。那只是在惹麻烦。如果你开始传递void指针,你还必须传递某种消息来指示指针类型是什么(或类型是什么)。这种技术很少适用。
如果您的参数始终相同,只需将它们添加到函数指针参数中(或者可能将它们打包到结构中,如果有很多参数,则将其用作参数)。如果您的参数发生了变化,那么请考虑为多个调用方案使用多个函数指针,而不是传递void指针。