2017-11-08 5 views
1

나는 c와 함수 포인터를 실험하고 있었다. 다음 코드는 컴파일러로서 gcc에서 잘 작동합니다.함수 포인터 C와 그 동작

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 유형을 선언했습니다. 또한 포인터없이 함수로 선언 할 수 있으며이 typename의 인스턴스에 대한 포인터를 사용할 수 있습니다.

먼저 형식 별칭을 사용하여 함수를 정의 할 수 없습니다. 이것은 구문 오류입니다.

확인 시험 인터넷 ER

는 특정한 함수 드 인터넷 nition의 선언자 부로 에드로 은 기능 유형을 가져야한다 (함수의 이름) 함수 드 인터넷 nition 선언. 138)

즉, 함수 정의는 다음과 같이만 볼 수 있습니다 :

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

gcc가 아마도 경고만으로 수락되었다는 사실은 gcc 확장입니다. 당신이 쓴 무엇

q = curry(p, 2); 

당신은 몇 가지 기능 프로그래밍을 알고 C에 적용하기를 원하지만 폐쇄의 개념과 지역 바인딩이 없기 때문에 C에서 curry 같은 동일한 개념은 다르게 적용, C 아니다 다른 함수를 반환하고 첫 번째 지역의 지역을 둘러싸는 함수가 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

C는 closure이라는 개념이 없으므로 일반 함수 포인터를 기반으로하는 카레 구현이 불가능합니다. 당신이 여기하려는 것은 폐쇄을 사용하고 있습니다 :

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가이를 확장으로 지원하지만 실제로 커링을 적용해야하는 경우에는 자신의 "함수 객체"를 정의해야합니다.

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

, 이것은 매우 빠르게 복잡해, 그래서 당신이 더 나은 모두 그 아이디어에 대해 잊지 제안 : 표준 C의 예는 다음과 같을 수 있습니다.

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

돌아갑니다. 함수 curried_f

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

세분화 결함을 일으키는, 그것은 x 및 F를 ACESS 시도라고

.