我想指向add(2, 1)
之类的东西然后调用它而不需要指定参数。有没有办法用函数指针做到这一点?
答案 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)
的函数,则必须在某处存储或硬编码值2
和1
,以便他们可以使用。
我可以想到有几种方法可以做到这一点。
创建一个调用add(2, 1)
的包装函数,让代码调用包装函数。
int add2And1()
{
return add(2, 1);
}
然后在通话中使用add2And1
。
创建一个依赖于全局数据的包装函数。在使用包装函数之前先设置全局数据。
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正在解决如何通过编写动态代码来传递带有函数指针的参数的问题。无论在那里写入什么代码,下一次函数调用深度都会变得足够深,堆栈帧将被覆盖。