在C中实现关闭

时间:2018-01-20 03:22:06

标签: c closures

我本质上是想在C中复制C#的Linq功能。

在C#中代码看起来像这样

// main
Coordinate coord = new Coordinate { x = 0, y = 0};
bool result = coordinates.Any(c => c.Data.Y == coord.Y || c.Data.X == coord.X);

在C中,我已经走到了这一步:

int any(struct linked_list *list, int (*cb)(struct node *node))
{
    .....
    do
    {
    if (cb)
        if ((*cb)(pNode))
            return 1;
    pNode = pNode->next;
    } while (pNode);
    return 0;
}

int compareCoordinates(struct node *node, struct coordinate *coord)
{
    struct coordinate *iterator = node->data;
    return iterator->x == coord->x || iterator->y == coord->y;
}
// main
struct coordinate coord;
int result = any(coordinates,compareCoordinates(??, coord));

我意识到将两个参数传递给一个带有一个参数的函数没有多大意义,但据我所知,在调用函数之前,参数会被添加到堆栈中,所以我认为必须有办法两个人都可以访问..我正在学习C语言以获得乐趣,而且我不确定谷歌到底是什么,因为这是我以前从未做过的事情。我想如果Linq库可以在C#中实现它,那么它应该可以在C中。是否有另一种方法可以从回调中访问局部变量?

编辑:经过一些更多的研究,我意识到我可以用嵌套函数实现我想要的东西,尽管我仍然很好奇,看看你是否可以传递更多功能而不是签名状态。

2 个答案:

答案 0 :(得分:0)

你可以从技术上将函数定义嵌套在GNU C中(注意,这不是一个闭包,因为它在父函数返回后不会保持coord):

int main(void) {
  struct coordinate coord;

  int compareCoordinates(struct node *node) {
    ...
  };

  any(list, compareCoordinates);
}

..但你真的不应该(非便携行为)。普通for循环更简单,更快速,更容易阅读,就像这种高阶函数的陷阱一样诱人。

另一方面,在C ++ 11及更高版本中,您可以使用lambda更接近原始C#模式:

any(list, [](struct node *node) -> bool { ... });

答案 1 :(得分:0)

我不知道C#,所以我真的不明白

bool result = coordinates.Any(c => c.Data.Y == coord.Y || c.Data.X == coord.X);

一样。但是看看你的C代码告诉我你正在使用一个函数指针, 第二个参数cb是一个函数指针。

我不知道你是否真的理解功能指针,所以我要解释一下 它很快:指针是一个存储内存位置地址的变量, 这可以是另一个变量的地址或动态地址 通过malloc分配内存。

C没有按引用调用,它总是按值调用,这意味着 函数的参数始终是原始的副本。通过传递 指示它可以模拟按引用调用,因为该函数 获取指针可以通过指针访问原始内存。

还有另一种指针:函数指针。这些就像 常规指针,但它们存储函数的位置。你经常使用它们 对于回调机制,例如qsort函数需要一个函数 指向用户提供的compare函数的指针。有了这个, qsort能够对任何类型的数组进行排序。

如果您希望compareCoordinates也可以访问另一个变量,那么 你必须将该变量传递给调用compareCoordinates的函数。

因此,您可以像这样重写any函数:

int any(struct linked_list *list, struct coordinate *coord, int (*cb)(struct node*, struct coordinate*))
{
    ....
    do
    {
        if (cb)
            if(cb(pNode, coord))
                return 1;
        pNode = pNode->next;
    } while (pNode);

    return 0;
}

// this function didn't change
int compareCoordinates(struct node *node, struct coordinate *coord)
{
    struct coordinate *iterator = node->data;
    return iterator->x == coord->x || iterator->y == coord->y;
}

// in main
int main(void)
{
    ...
    struct coordinate coord;
    // here you should initialize coord (unless you do that in
    // the any function. It's not clear from your snippet

    int result = any(coordinates, &coord, compareCoordinates);

    ...
}

首先看看函数any。要使用函数指针,您不需要 取消引用它(意思是使用* - 运算符)。我还改变了cb的声明, 以便它与compareCoordinates匹配。另外any还需要一个 参数:指向strcut coordinate的指针。

现在,当您想要呼叫any时,您必须传递3个参数:列表, 指向struct coordinate的指针(& - 运算符返回变量的地址) 以及您希望any调用以进行比较的功能。请注意,按顺序 要将函数传递给另一个函数,您只需使用该名称。

最后一件事:struct coordinate coord;只声明变量,而不是 初始化。在某些时候你必须初始化它。

修改

另一件小事:当你声明一个函数指针时,你不需要 命名参数。你可以这样做,但没有必要。

int (*func1)(int a, int b, int c);  // that's fine

int (*func1)(int, int, int); // that's also correct

我更喜欢第二个,你必须输入更少而且它增加(至少对我而言) 代码的可读性。