【问题标题】:Passing more parameters in C function pointers在 C 函数指针中传递更多参数
【发布时间】:2010-09-05 21:29:51
【问题描述】:

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

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 函数用途广泛,代码中的许多不同位置都引用了它。这些引用中的每一个都必须更新它们的函数以包含它们不需要的参数是没有意义的。

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

【问题讨论】:

    标签: c architecture pointers


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

        【解决方案3】:

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

        那个函数发生了什么?你有条件说,

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

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

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

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

        【讨论】:

          【解决方案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 的其他用途。

          【讨论】:

            【解决方案5】:

            我建议使用 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 结尾的数组。

            【讨论】:

              【解决方案6】:

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

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

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

              【讨论】:

                【解决方案7】:

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

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

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

                【讨论】:

                  【解决方案8】:

                  对函数指针使用 typedef。看到我的answerthis question

                  【讨论】:

                    【解决方案9】:

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

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2010-09-29
                      • 1970-01-01
                      • 2014-04-26
                      • 2012-05-03
                      • 2022-01-20
                      • 2013-04-28
                      • 1970-01-01
                      相关资源
                      最近更新 更多