你能在 C 中拥有带指定参数值的函数指针吗?
Can you have function pointer with assigned parameter value in C?
我想指向类似add(2, 1)
的东西,然后调用它而不需要指定参数。有什么方法可以用函数指针做到这一点吗?
int add(int a, int b) { return a + b; }
不,您不能拥有指向 add
并为其分配参数值的指针。但是你可以这样做:
int add_2_1() { return add(2, 1); }
但是我看不出这有什么用...
虽然简短的回答是 "no",但有多种方法可以完成您想要做的事情。
首先要记住的是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
.
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 正在解决如何通过编写动态代码来传递带有函数指针的参数的问题。不管那里写了什么代码,下次函数调用深度足够深时,堆栈帧将被覆盖。
作为对您问题的评论,Basile Starynkevitch suggested reading about closures。闭包的核心是将要调用的函数与某些上下文结合起来,在文献中称为函数的 environment.
Object-oriented 语言有一个类似的概念,称为 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
将闭包分配 space 的问题留给了它的调用者。
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.
进一步阅读
我想指向类似add(2, 1)
的东西,然后调用它而不需要指定参数。有什么方法可以用函数指针做到这一点吗?
int add(int a, int b) { return a + b; }
不,您不能拥有指向 add
并为其分配参数值的指针。但是你可以这样做:
int add_2_1() { return add(2, 1); }
但是我看不出这有什么用...
虽然简短的回答是 "no",但有多种方法可以完成您想要做的事情。
首先要记住的是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
.
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 正在解决如何通过编写动态代码来传递带有函数指针的参数的问题。不管那里写了什么代码,下次函数调用深度足够深时,堆栈帧将被覆盖。
作为对您问题的评论,Basile Starynkevitch suggested reading about closures。闭包的核心是将要调用的函数与某些上下文结合起来,在文献中称为函数的 environment.
Object-oriented 语言有一个类似的概念,称为 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
将闭包分配 space 的问题留给了它的调用者。
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.