2017-11-08 15 views
1

私は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; 
} 

プログラムがセグメンテーションフォルトをスローします。私はなぜ、どのように理解できないのですか?説明は本当にいいですね。

+0

私はあなたがここでやろうとしていることを完全には得ていませんが、あなたのコードにはネストされた関数*があり、Cには存在しません。 –

+2

@FelixPalmen GCC拡張です。本当にカレーにすることができるかどうかは分かりませんが、それは魔法のようです。 – unwind

+1

どこが違うのですか?デバッガから何を伝えましたか? –

答えて

0

a pointer to a function int->intを表すタイプエイリアスone_var_funcを宣言しました。関数をポインタなしで宣言し、この型名のインスタンスへのポインタを使用することもできます。

まず、タイプエイリアスを使用して関数を定義することはできません。これは構文エラーです。

機能ドのfi nitionの宣言部で編特異的のように、関数型を持たなければならない識別子は、(関数の名前である)関数ドのfi nitionに宣言しました。 138)

すなわち関数定義は、だけなので見に許可されている:

function-definition: 
    declaration-specifiers declarator declaration-listopt compound-statement 

gccが警告だけで、おそらく受け入れられているという事実は、これはgccの拡張機能です。

q = curry(p, 2); 

何が書いたことはあなたには、いくつかの関数型プログラミングを知っているし、Cでそれを適用したい、Cではなく、閉鎖の概念との局所的な束縛がないのでCでcurryのような同じ概念が異なって適用されています別の関数を返し、最初のローカルを囲む関数がC言語のカーネルにない場合、それらを自分でエンコードする必要があります。

qがローカルバインディング2に直接アクセスする方法はありません。

e = apply(* q, 10); 

同じ、qを評価するためには、あなたがq内でエンコードされなければならないいくつかのローカル環境を必要とする10に適用されます。

CIについてlispの評価者を実装しようとする前に、あなたは最初にCを学び、後でhereから始まるlispの実装について読んでみることをお勧めします。 。

2

入れ子関数はgcc拡張です。 gccのドキュメントでは、インクルード関数の呼び出しが終了すると、ネストされた関数へのポインタは、少なくともアップレベルの変数参照を試みると、無効になります。限り含む機能がアクティブであるように、そのローカル変数が割り当てられたままとネストされた関数からの最大レベルの参照が解決できるため

このことは、理にかなっています。しかし、いったん包含する関数が終了すると、スタックフレームを保存するためにクロージャをサポートする必要があります。

4

ネストされた関数は、標準Cには存在しないGCCの拡張機能であるため、(質問など)この答えは、GCC固有のものです。

Cのネストされた関数はクロージャを提供しません。つまり、ネストされた関数は、外部関数が返るまで外部関数のローカル変数にしかアクセスできません。 GCC documentationがこの主題に言って、次があります

あなたはすべての地獄の休憩が緩んで、含む関数が終了した後にそのアドレスを使用して、ネストされた関数を呼び出すしよう。スコープ・レベルが終了した後にコールしようとすると、スコープ・レベルがもはや存在しない変数の一部を参照している場合、あなたは幸運かもしれませんが、リスクを取るのは賢明ではありません。ただし、ネストされた関数が範囲を外れたものを参照しない場合は、安全でなければなりません。

両方のバージョンのコードがこのルールに違反していますが、なぜ1つのみがセグメンテーション違反ですか?一つの答えは、「未定義の振る舞い」と同じように、「すべての地獄が緩んでいる」とは、一見期待通りに働くことを含め、あらゆるタイプの振る舞いを記述できるということです。他の機能はそれらをオーバーライドするまで、彼らはスタックスペースを必要とするときの値がちょうどそこに滞在 -

より実装指向の答えは、関数から返すことは実際にすぐにスタックにその内容を消去しないことです。新しいローカル変数を導入すると、関数はより多くのスタック領域を必要とするため、2つ目の関数は以前のバージョンでは使用できなかったスタックメモリをオーバーライドします。

1

Cには、クロージャの概念がないため、プレーン関数ポインタに基づいてカリングを実装することができません。これは、ネストされた関数はxの値を「取り込み」ことを意味する

one_var_func curry(int(* f)(int, int), int x) { 
    int curried_f(int y) { 
    return f(x, y); 
    } 
    return (curried_f); 
} 

:あなたがここにしようとしていることは閉鎖を使用しています。しかし、Cでは、実行が自動的に囲みスコープを離れると、自動記憶期間を持つ変数は存在しなくなり、閉鎖の概念はありません。

GCCは、あなたが本当にカリー化を適用する必要がある場合、あなたはあなた自身の「関数オブジェクト」を定義する必要があり、拡張機能としてそれをサポートしていますが、でも、ネストされた関数は、Cに存在しないことを考えます。標準 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("%d\n", intfunc_call(multiply_by_two, 10)); 
    free(multiply_by_two.ctx); 
    return 0; 
} 

が、これは非常に迅速に複雑になるので、私はあなたがより良い完全にそのアイデアを忘れる示唆しています。

0

それはローカル変数Fませんし、xはスタック上にもはやある関数が

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); 
} 

戻ります。

int curried_f(int y) 
{ 
    return f(x, y); //f and x are local to curry 
} 

curried_f機能は、それがセグメンテーション違反を引き起こす、xおよびFをアセスしようと呼ばれています。

関連する問題