C 函数指针:从函数 [duplicate] 返回函数指针

C function pointer: returning a function pointer from a function [duplicate]

提问人:uvero 提问时间:12/21/2022 更新时间:12/21/2022 访问量:177

问:

我目前正在学习 C,并试图使我的代码更具可读性和更易于编写,我发现自己需要在函数中创建一个函数(闭包),并返回该函数。这是我要做的:

#typedef int (int_predicate*)(int);
int_predicate equals(int x) {
  int ret(int y) { return x == y; }
  return ret;
}

现在,这个不起作用,我明白了:我正在一个函数中创建这个闭包 (ret),所以一旦这个函数返回,指针就不再相关,因为它是在堆栈上定义的。这类似于我为更简单类型的指针执行此操作:

int* bad_int_ptr_function() {
  int intOnTheStack;
  int* ptr = &intOnTheStack;
  return ptr;
}

那么,我怎样才能实现我想要的 - 在函数中创建一个闭包并对其进行重构?如果所指向的类型的大小是已知的,我可以使用,如果我想,我可以用 .但是,我只能假设函数的大小不能用 sizeof 找到,因为函数可以有任意数量的行。mallocmemcpy

这是 C,我不能使用特定于 C++ 的语法或库(我听说 C++ 最近得到了 lambda 表达式 - 如果我能使用 C++,但我不能,如果用于特定于 C 的课程)。

C 分段-故障 闭包 -指针 匿名-函数

评论

4赞 Brian61354270 12/21/2022
请注意,在标准 C 中,以任何形状或形式(无论是否闭包)定义嵌套函数是不可能的。另请参阅 C 语言中的嵌套函数
1赞 Eric Postpischil 12/21/2022
我希望 GCC 定义嵌套函数的功能不支持任何导出嵌套函数以在包含函数之外使用的方法。这是因为它使用堆栈上的空间来实现嵌套函数(可能包括将可执行代码放在堆栈上),因此当包含函数返回时,空间将变为未保留。Clang 的 Blocks 功能也许可以做到这一点。
0赞 Eric Postpischil 12/21/2022
@Brian:在标准(符合)C 中可以定义嵌套函数,因为 C 标准允许并邀请扩展。严格遵守 C 是不可能的。但是,当然,严格遵守 C 语言(例如调用或其他操作系统例程)的可能性很小。write
0赞 Eric Postpischil 12/21/2022
另一种方法是定义一个嵌套函数,然后从包含函数中调用将使用它的函数。
2赞 hyde 12/21/2022
@EricPostpischil还是有区别的。如果可以从库中获得(甚至其他标准(如 POSIX)要求),您可能找不到不允许在该平台上调用它的 C 编译器。但是,支持嵌套函数的扩展在任何平台上都不会如此普遍。write

答:

0赞 Andrew Henle 12/21/2022 #1

在 C 语言中没有(简单、健壮的)方法来创建闭包。

但有一种方法可以稍微接近:你可以返回一个包含函数指针的函数指针,该指针可以将 or 的地址作为参数:structstruct

typedef int ( *intClosureFuncPtr )( struct intClosure * );

struct intClosure
{
    int tag // can be used to differentiate multiple struct
            // types in a union
    intClosureFuncPtr func;
        .
        .
    // fields as necessary
};

闭合功能:

int closureFunc0( struct intClosure *c )
{
       .
       .
       .
    return( 0 );
}

int closureFunc42( struct intClosure *c )
{
       .
       .
       .
    return( 42 );
}

调用创建闭包并返回 :struct

struct intClosure someFunc( ... )
{

    struct intClosureInstance = { 0 };

    intClosure.tag = 7;
    intClosure.func = closureFunc42;
    // fill in rest of the struct fields as needed 

    return( intClosureInstance );
}

用法:

struct intClosure closureInstance = someFunc(...);
    .
    .
    .

// call the closure:
int result = closureInstance.func( &closureInstance );

也可以按地址返回它,但这需要调用者。您需要评估“闭包”的大小,并且必须按值传递它,以应对要求调用者的额外复杂性。malloc()structfree()structfree()

请注意,此“闭包”的返回类型是固定的。有些黑客可能允许在 POSIX 和 Windows 系统上动态选择返回类型,因为它们都有一个由所有变量和函数共享的平面地址空间(当前 Windows 共享),但这些黑客将深入研究 C 标准未定义的行为。

* - 如果您愿意将 C“闭包”限制为仅从创建它们的线程中调用,并且每个线程在任何时刻都只能调用一个未完成的“闭包”,则可以使用特定于线程的数据将信息传递给生成闭包时由函数指针返回的实际函数,然后可以通过上述方法直接调用该函数,而无需任何间接调用。可能有一些方法可以允许更复杂的使用模式,但我目前无法想出任何方法。struct

1赞 chqrlie 12/21/2022 #2

在标准 C 中,不能将闭包定义为函数,但可以使用数据结构模拟它们:

#include <stdio.h>

typedef struct int_predicate_t {
    int (*func)(struct int_predicate_t *s, int x);
    int y, z;
} int_predicate_t;

#define APPLY(p, x)  ((p).func(&(p), x))

int equal_func(struct int_predicate_t *s, int x) {
    return x == s->y;
}

int greater_func(struct int_predicate_t *s, int x) {
    return x > s->y;
}

int between_func(struct int_predicate_t *s, int x) {
    return x >= s->y && x <= s->z;
}

int_predicate_t equals(int y) {
    int_predicate_t s = { equal_func, y, 0 };
    return s;
}

int_predicate_t greater(int y) {
    int_predicate_t s = { greater_func, y, 0 };
    return s;
}

int_predicate_t between(int y, int z) {
    int_predicate_t s = { between_func, y, z };
    return s;
}

int main() {
    int_predicate_t p1 = equals(42);
    int_predicate_t p2 = greater(10);
    int_predicate_t p3 = between(1, 100);

    printf("p1(42) = %d\n", APPLY(p1, 42));
    printf("p2(42) = %d\n", APPLY(p2, 42));
    printf("p3(42) = %d\n", APPLY(p3, 42));
    printf("p1(0) = %d\n", APPLY(p1, 0));
    printf("p2(0) = %d\n", APPLY(p2, 0));
    printf("p3(0) = %d\n", APPLY(p3, 0));

    return 0;
}