关于体系结构:在C函数指针中传递更多参数

关于体系结构:在C函数指针中传递更多参数

Passing more parameters in C function pointers

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

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指针。


    推荐阅读

      opporeno8参数配置及价格

      opporeno8参数配置及价格,面部,亿元,Oppo的荣誉2020年1月4日,接近屏幕关闭传感器是否支持双卡:支持oppor11splus什么时候上市的Oppo R11S P

      魅蓝note6性能参数有哪些

      魅蓝note6性能参数有哪些,摄像头,蓝牙,魅蓝note6性能参数有哪些魅力蓝色Note6最好拍照。电池寿命更长。蓝色Note6使用高通 snapdragon 625

      设置总账参数|用友u8设置总账参数

      设置总账参数|用友u8设置总账参数,,1. 用友u8设置总账参数1、首先要点开数据权限控制设置;2、选择想要设置控制的单据;3、打开后看到左上角

      联想垫a2207如何调用a2207平板评价

      联想垫a2207如何调用a2207平板评价,,联想a2207垫在1600 3G双网双待平板电脑的位置。作为以前的热门产品a2107升级版,a2207处理器取得了可观

      csgo参数设置|csgo怎么保存

      csgo参数设置|csgo怎么保存,,csgo怎么保存第一步下载csgo的官方版本。然后再下载一个5e对战平台,PS:5e的账号和csgo的账号不是一个账号。第

      移动apn设置|移动apn设置参数

      移动apn设置|移动apn设置参数,,移动apn设置参数1、打开手机系统设置界面应用,点击页面中的“移动网络”设置选项。2、进入移动网络设置页面

      均线最佳设置|均线最佳参数设置

      均线最佳设置|均线最佳参数设置,,1. 均线最佳参数设置所有指标包括kd macd均线,是根据已有价格计算得出,看指标落后于看价格分析,也就是k线。