我在试验c和函数指针。下面的代码可以很好地使用gcc作为编译器。
typedef int( * one_var_func)(int);
int Multiply(int x, int y) {
return x * y;
}
one_var_func curry(int( * f)(int, int), int x) {
int curried_f(int y) {
return f(x, y);
}
return (curried_f);
}
int apply(int( * f)(int), int x) {
return f(x);
}
int main() {
int( * p)(int, int);
one_var_func q;
int e;
p = & Multiply;
q = curry(p, 2);
e = apply( * q, 10);
printf("%d n", e);
return 1;
}
然而,当我做这个小修改时;
int apply(int (*f)(int) ,int x){
int a;
a=f(x)
return a;
}
程序抛出分段错误。我不明白为什么和怎么做。解释一下就好了。
嵌套函数是标准C中不存在的GCC扩展,因此这个答案(就像问题一样)是特定于GCC的。
C中的嵌套函数不提供闭包。也就是说,嵌套函数只能访问外部函数的局部变量,直到外部函数返回为止。GCC文件对此有以下内容:
如果在包含函数退出后试图通过其地址调用嵌套函数,那么所有的地狱都会崩溃。如果您试图在包含作用域级别退出后调用它,并且它引用了一些不再在作用域中的变量,那么您可能很幸运,但冒险是不明智的。但是,如果嵌套函数没有引用任何超出范围的内容,则应该是安全的。
您的代码的两个版本都违反了此规则,那么为什么只有一个版本会导致segfault呢?一个答案是,就像"未定义的行为"一样,"一切都会失控"可以描述所有类型的行为,包括看似按预期工作的行为。
更面向实现的答案是,从一个函数返回实际上不会立即擦除堆栈上的内容——这些值只是停留在那里,直到另一个函数在需要堆栈空间时覆盖它们。引入一个新的局部变量会使函数需要更多的堆栈空间,因此您的第二个函数会覆盖以前版本没有的堆栈内存。
嵌套函数是gcc扩展。gcc文档指出,一旦包含函数调用退出,指向嵌套函数的任何指针都将无效,至少在它们尝试任何高级变量引用时是这样。
这是有道理的,因为只要包含函数处于活动状态,它的局部变量就会保持分配状态,并且可以解析嵌套函数的上级引用。但是,一旦包含函数退出,它就需要支持闭包来保留堆栈框架,而事实并非如此。
C没有闭包的概念这使得无法实现基于普通函数指针的currying。您在这里尝试的是使用闭包:
one_var_func curry(int( * f)(int, int), int x) {
int curried_f(int y) {
return f(x, y);
}
return (curried_f);
}
这意味着嵌套函数"捕获"x
的值。但在C中,一旦执行离开封闭范围,任何具有自动存储持续时间的变量都不存在了,而且没有闭包的概念可以阻止这种情况的发生。
考虑到C中甚至不存在嵌套函数,尽管GCC支持将其作为扩展,但如果您真的需要应用currying,则必须定义自己的"函数对象"。标准C中的一个示例可能如下所示:
#include <stdio.h>
#include <stdlib.h>
typedef struct intfunc
{
int (*f)();
void *ctx;
} intfunc;
typedef struct curryctx
{
int (*f)();
int x;
} curryctx;
static int currycall(void *ctx, int x)
{
curryctx *cctx = ctx;
return cctx->f(cctx->x, x);
}
int intfunc_call(intfunc f, int x)
{
return f.ctx ? f.f(f.ctx, x) : f.f(x);
}
intfunc createfunc(int (*f)())
{
return (intfunc){f, 0};
}
intfunc curryfunc(int (*f)(), int x)
{
curryctx *cctx = malloc(sizeof *cctx);
if (!cctx) exit(1);
cctx->f = f;
cctx->x = x;
return (intfunc){currycall, cctx};
}
static int multiply(int x, int y)
{
return x*y;
}
int main()
{
intfunc multiply_by_two = curryfunc(multiply, 2);
printf("%dn", intfunc_call(multiply_by_two, 10));
free(multiply_by_two.ctx);
return 0;
}
当然,这很快就会变得复杂,所以我建议你最好完全忘记这个想法。
您声明了类型别名one_var_func
来表示a pointer to a function int->int
。您也可以将它声明为不带指针的函数,并使用指向该类型名称实例的指针。
首先,不允许使用类型别名定义函数。这是一个语法错误。
函数定义中声明的标识符(即函数名称)应有一个函数类型,由函数定义的声明符部分指定。138)
例如,函数定义只允许看起来是这样的:
function-definition:
declaration-specifiers declarator declaration-listopt compound-statement
事实上,gcc可能只接受了一个警告,这是gcc的扩展。
q = curry(p, 2);
你写的不是C,你知道一些函数式编程,并想在C中应用它,但在C中,像curry
这样的相同概念的应用不同,因为没有闭包的概念,并且返回另一个函数并包含第一个函数的局部的函数的局部绑定不在C语言的内核中,你需要自己对它们进行编码。
CCD_ 5没有直接的方式访问本地绑定CCD_。
e = apply( * q, 10);
同样,要评估应用于10
的q
,您需要一些本地环境,这些环境应该在q
中进行编码。
关于你的代码还有其他的事情要说,在你尝试在C中实现lisp的评估器之前,我建议你首先学习C,然后从这里开始阅读lisp的实现,因为它们不是不言自明的。
当函数时
one_var_func curry(int( * f)(int, int), int x)
{
int curried_f(int y)
{
return f(x, y); //f and x are local to curry
}
return (curried_f);
}
返回它的局部变量f和x不再在堆栈中。
当函数curried_f
int curried_f(int y)
{
return f(x, y); //f and x are local to curry
}
被称为试图访问x和f,这会导致分割错误。