你能用C指定参数值的函数指针吗?

时间:2018-03-16 16:20:43

标签: c function-pointers

我想指向add(2, 1)之类的东西然后调用它而不需要指定参数。有没有办法用函数指针做到这一点?

4 个答案:

答案 0 :(得分:14)

作为对您问题的评论,Basile Starynkevitch建议阅读closures。从本质上讲,闭包结合了一个被调用的函数和一些上下文,在文献中称为函数的环境

面向对象语言具有类似的概念,称为delegates,其中特定实例可以成为稍后调用的环境,其中调用站点不直接了解底层对象。支持闭包的语言本身自动捕获或“关闭”环境中提供的绑定。它似乎是一种奇怪的语言特征,但它可以表达和有用,因为你的问题背后的动机暗示。

下面是C程序中闭包概念的一个简单示例,由程序员明确指示捕获。

您最终要调用的功能加上一些前沿内容

#include <stdio.h>

int
add_two_numbers(int a, int b)
{
  return a+b;
}

闭包是与其环境结合调用的函数。在C中,要调用的函数是指向函数的指针。请注意,f的参数与add_two_numbers的参数一致。

typedef struct {
  struct {  /* environment */
    int a;
    int b;
  } env;

  int (*f)(int, int);  /* function to be called */
} closure;

我们想创建一个闭包,,设置参数值的关联,以便在我们准备好时调用要调用的函数。在这个简单的例子中,make_adder留下了为闭包分配空间的问题。

void
make_adder(int a, int b, closure *c)
{
  c->env.a = a;
  c->env.b = b;
  c->f = add_two_numbers;
}

既然您知道如何创建我们的简单闭包之一,那么您可以在

中调用或调用
int invoke_closure(closure *c)
{
  return c->f(c->env.a, c->env.b);
}

最后,用法看起来像

int main(void)
{
  closure c;
  make_adder(2, 1, &c);

  printf("The answer is %d.\n", invoke_closure(&c));

  return 0;
}

输出

The answer is 3.

进一步阅读

答案 1 :(得分:8)

虽然简短的回答是“不”,但有办法完成你想做的事。

首先要记住的是add需要两个参数。无论你做什么,你都必须提供两个参数。

要记住的第二件事是,如果要调用最终调用add(2, 1)的函数,则必须在某处存储或硬编码值21,以便他们可以使用。

我可以想到有几种方法可以做到这一点。

  1. 创建一个调用add(2, 1)的包装函数,让代码调用包装函数。

    int add2And1()
    {
       return add(2, 1);
    }
    

    然后在通话中使用add2And1

  2. 创建一个依赖于全局数据的包装函数。在使用包装函数之前先设置全局数据。

    int param1 = 0;
    int param2 = 0;
    
    
    int addParams()
    {
       return add(param1, param2);
    }
    

    然后使用:

    param1 = 2;
    param2 = 1;
    
    在调用addParams之前,在代码中

答案 2 :(得分:6)

int add(int a, int b) { return a + b; }

不,你没有指向add指针的指针。但是你可以这样做:

int add_2_1() { return add(2, 1); }

然而我看不出这有多大帮助...

答案 3 :(得分:1)

GCC扩展程序允许您这样做:

int func(int addend, int (*next)(int (*f2)(int))
{
    int add(int addend2) { return addend + addend2; }
    next(add);
}

但是,如果您尝试这样做:

int (*)(int) func(int addend)
{
    int add(int addend2) { return addend + addend2; }
    return add;
}

无法使用该函数,因为func(3)(3)在已释放的堆栈上执行trampoline *。这几乎是最不确定的行为。

*蹦床是一小段代码,可立即跳转到另一段代码。在这种情况下,蹦床看起来像

mov rax, 0xfuncstackframeaddress
mov r10, rax
lea rax, [func.add]
jmp rax

当然有很多方法可以做到这一点,GCC使用哪个并不重要。 GCC正在解决如何通过编写动态代码来传递带有函数指针的参数的问题。无论在那里写入什么代码,下一次函数调用深度都会变得足够深,堆栈帧将被覆盖。