2017-11-08 4 views
1

Ich experimentierte mit c und Funktion Zeiger. Das folgende Stück Code funktioniert gut mit gcc als Compiler.Funktion Pointer in C und ihr Verhalten

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

Allerdings, wenn ich diese geringfügige Änderung mache;

int apply(int (*f)(int) ,int x){ 
    int a; 
    a=f(x) 
    return a; 
} 

Programm löst einen Segmentierungsfehler aus. Ich verstehe nicht warum und wie. Eine Erklärung wäre wirklich nett.

+0

Ich bekomme nicht vollständig, was Sie hier versuchen, aber Sie haben eine * geschachtelte Funktion * in Ihrem Code, etwas, das nicht in C existiert. –

+2

@FelixPalmen Es ist eine GCC-Erweiterung. Keine Ahnung, ob es wirklich zum Curry gemacht werden kann, das wirkt magisch. – unwind

+1

Wo segfault? Was hat dein Debugger dir gesagt? –

Antwort

0

Sie haben den Typalias one_var_func für a pointer to a function int->int deklariert. Sie können es auch ohne Zeiger als Funktion deklarieren und den Zeiger auf die Instanz dieses Typs verwenden.

Zunächst dürfen Sie keine Funktion mit Typaliasen definieren. Dies ist ein Syntaxfehler.

Die Kennung deklariert in Funktion de Definition (die der Name der Funktion ist) wird einen Funktionstyp haben, wie spezifische durch den declarator Teil der Funktion de Definition ed. 138)

D.h. eine Funktionsdefinition ist nur so aussehen erlaubt:

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

Die Tatsache, dass gcc akzeptiert wahrscheinlich mit einer Warnung nur, diese gcc Erweiterung.

q = curry(p, 2); 

Was schrieb man nicht C, wissen Sie einige funktionale Programmierung und wollen es in C bewerben, aber in C die gleichen Konzepte wie curry unterschiedlich angewendet, da es kein Konzept der Schließung ist und die lokalen Bindungen Eine Funktion, die eine andere Funktion zurückgibt und die Locals des ersten enthält, befindet sich nicht im Kernel der C-Sprache, Sie müssen sie selbst codieren.

Es gibt keinen direkten Weg für q, um auf die lokale Bindung zuzugreifen 2.

e = apply(* q, 10); 

Das gleiche, bewerten q auf 10 angewendet Sie einige lokale Umgebungen benötigen, die innerhalb q codiert werden soll.

Es gibt andere Dinge zu Ihrem Code zu sagen, bevor Sie versuchen, Evaluatoren von Lisp in CI zu implementieren vorschlagen, dass Sie zuerst lernen C und danach über die Implementierungen von Lisp ab here zu lesen, da sie nicht offensichtlich sind .

2

Verschachtelte Funktionen sind eine gcc-Erweiterung. Die GCC-Dokumentation besagt, dass alle Pointer auf die verschachtelte Funktion ungültig werden, sobald der Aufruf der aufschließenden Funktion beendet wurde, zumindest wenn sie irgendwelche Up-Level-Variablenreferenzen versuchen.

Dies ist sinnvoll, da solange die containing-Funktion aktiv ist, ihre lokalen Variablen zugewiesen bleiben und die auf höheren Ebenen liegenden Referenzen der verschachtelten Funktion aufgelöst werden können. Aber sobald die containing-Funktion beendet wird, müsste sie Schließungen unterstützen, um den Stapelrahmen zu erhalten, was nicht der Fall ist.

4

Verschachtelte Funktionen sind eine GCC-Erweiterung, die in Standard C nicht existiert. Daher ist diese Antwort (wie die Frage) GCC-spezifisch.

Verschachtelte Funktionen in C bieten keine Verschlüsse. Das heißt, eine verschachtelte Funktion kann nur auf lokale Variablen der äußeren Funktion zugreifen, bis die äußere Funktion zurückkehrt. Die GCC documentation hat folgende zu diesem Thema zu sagen:

Wenn Sie versuchen, die verschachtelte Funktion durch seine Adresse nach der enthaltenden Funktion beendet zu nennen, bricht die Hölle los. Wenn Sie versuchen, es aufzurufen, nachdem eine enthaltende Gültigkeitsbereichsebene beendet wurde, und wenn es sich auf einige der Variablen bezieht, die nicht mehr im Geltungsbereich sind, können Sie Glück haben, aber es ist nicht ratsam, das Risiko einzugehen. Wenn sich die verschachtelte Funktion jedoch nicht auf etwas bezieht, das den Gültigkeitsbereich verlassen hat, sollten Sie sicher sein.

Beide Versionen Ihres Codes verstoßen gegen diese Regel. Warum verursacht nur einer einen segfault? Eine Antwort ist, dass genau wie "undefiniertes Verhalten" "die Hölle losbricht" alle Arten von Verhalten beschreiben kann, einschließlich der scheinbaren Arbeit wie erwartet. Die implementierungsorientierte Antwort lautet, dass das Zurückkehren von einer Funktion ihren Inhalt auf dem Stapel nicht sofort löscht - die Werte bleiben einfach dort, bis eine andere Funktion sie außer Kraft setzt, wenn sie den Stapelraum benötigen. Wenn Sie eine neue lokale Variable einführen, benötigt die Funktion mehr Stapelspeicherplatz. Ihre zweite Funktion überschreibt also den Stapelspeicher, den die vorherige Version nicht hatte.

1

C nicht über ein Konzept der Verschlüsse, die es unmöglich macht, currying auf Normalfunktionszeiger zu implementieren basiert. Was Sie versuchen, hier wird mit einem Schließung:

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

Dies würde bedeuten, dass die verschachtelte Funktion „einfängt“, um den Wert von x. Aber in C existiert jede Variable mit automatischer Speicherdauer nicht mehr, sobald die Ausführung den umschließenden Bereich verlässt, und es gibt kein Konzept von Closures, das dies verhindern könnte.

Vorausgesetzt, dass selbst eine geschachtelte Funktion in C nicht existiert, obwohl GCC sie als Erweiterung unterstützt, müssen Sie, wenn Sie wirklich currying anwenden müssen, ein eigenes "Funktionsobjekt" definieren.Ein Beispiel in Standard C könnte wie folgt aussehen:

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

Natürlich ist dies recht schnell komplexer wird, so schlage ich vor, Sie besser über diese Idee zu vergessen überhaupt.

0

wenn Funktion

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

kehrt es lokale Variablen f ist und x nicht mehr auf dem Stapel.

wenn Funktion curried_f

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

genannt wird versucht, x und f Acess, die Segmentierungsfehler verursacht.