programing

복잡한 함수 선언을 어떻게 이해해야 합니까?

css3 2023. 10. 12. 23:26

복잡한 함수 선언을 어떻게 이해해야 합니까?

복잡한 선언을 따르는 것을 어떻게 이해할 수 있습니까?

char (*(*f())[])();

char (*(*X[3])())[5];

void (*f)(int,void (*)()); 

char far *far *ptr;

typedef void (*pfun)(int,float);

int **(*f)(int**,int**(*)(int **,int **));

다른 사람들이 지적한 것처럼 cdecl은 그 일에 적합한 도구입니다.

cdecl의 도움 없이 그런 선언을 이해하고 싶다면, 안쪽에서 오른쪽에서 왼쪽으로 읽어보세요.

목록에서 하나의 임의 예제 가져오기
선언/정의되는 식별자(그리고 가장 안쪽의 식별자)인 X에서 시작합니다.

char (*(*X[3])())[5];
         ^

X는

X[3]
 ^^^

X는 삼열

(*X[3])
 ^                /* the parenthesis group the sub-expression */

X는 3의 배열입니다. 에 대한 지시.

(*X[3])()
       ^^

X는 3개의 포인터의 배열입니다. 함수는 지정되지 않은(그러나 고정된) 인수 개수를 허용합니다.

(*(*X[3])())
 ^                   /* more grouping parenthesis */

X는 지정되지 않은(고정된) 수의 인수를 수용하는 함수에 대한 3개의 포인터 배열입니다. 그리고 포인터를 돌려주기

(*(*X[3])())[5]
            ^^^

X는 함수에 사용할 3개의 포인터의 배열로 불특정 다수의 인수를 허용하고 포인터를 반환합니다. 5명으로

char (*(*X[3])())[5];
^^^^                ^

X는 3개의 포인터의 배열로 기능하여 불특정 다수의 인수를 허용하고 5 char배열포인터를 반환합니다.

와 같은 .{3+5*[2+3*(x+6*2)]}=0- 당신은 그 안에 있는 것을 해결하는 것으로 시작할 것입니다.()그리고나서[]y{}:

char (*(*x())[])()
         ^

은.x뭔가.

char (*(*x())[])()
          ^^

x함수입니다.

char (*(*x())[])()
        ^

x포인터를 반환합니다.

char (*(*x())[])()
       ^    ^^^

x배열에 포인터를 반환합니다.

char (*(*x())[])()
      ^

x포인터를 포인터 배열로 반환합니다.

char (*(*x())[])()
     ^         ^^^

x함수에 대한 포인터 배열에 포인터를 반환합니다.

char (*(*x())[])()
^^^^

합니다에서 한 배열 합니다.x문자를 반환하는 함수를 가리키는 함수 포인터 배열을 가리킵니다.

하지만 네, cdecl을 사용합니다.답변 확인을 위해 직접 사용했습니다 :).

만약 이것이 여전히 여러분을 혼란스럽게 하고 있다면, 종이나 좋아하는 텍스트 편집기에서 같은 작업을 해보세요.보기만 해도 무슨 뜻인지 알 길이 없습니다.

cdecl 도구의 작업처럼 들립니다.

cdecl> explain char (*(*f())[])();
declare f as function returning pointer to array of pointer to function returning char

툴 공식 홈페이지를 찾아봤지만 정품으로 보이는 홈페이지를 찾을 수 없었습니다.Linux에서는 일반적으로 도구를 포함하여 선택한 분포를 기대할 수 있으므로 위의 샘플을 생성하기 위해 설치한 것입니다.

cdecl 도구를 사용해야 합니다.대부분의 리눅스 배포판에서 사용할 수 있어야 합니다.

예를 들어 이 기능의 경우 다음을 반환합니다.

char (*(*f())[])();의 함수 반환 포인터로 합니다를 합니다.

void (*f)(int,void (*)());- 함수 포인터 f 의 프로토타입은 두 개의 파라미터를 취하는 함수이며, 첫 번째 파라미터는 int이고, 두 번째 파라미터는 void를 반환하는 함수의 함수 포인터입니다.

char far *far *ptr;- ptr은 먼 포인터(일부 문자/바이트를 가리킴)를 가리키는포인터입니다.

char (*(*X[3])())[5];5로 되돌리는 - X는할 3를 5 char합니다.

typedef void (*pfun)(int,float);을 선언합니다.은 두 변수를 변수는 입니다. -터 pfun합니다. pfun입니다. int는 float입니다. 다.

예.

void f1(int a, float b)
{ //do something with these numbers
};

그건 그렇고, 마지막으로 복잡한 선언은 자주 볼 수 없습니다.이를 위해 제가 방금 만든 예시가 있습니다.

int **(*f)(int**,int**(*)(int **,int **));

typedef int**(*fptr)(int **,int **);

int** f0(int **a0, int **a1)
{
    printf("Complicated declarations and meaningless example!\n");
    return a0;
}

int ** f1(int ** a2, fptr afptr)
{
    return afptr(a2, 0);
}

int main()
{
    int a3 = 5;
    int * pa3 = &a3;
    f = f1;
    f(&pa3, f0);

    return 0;
}

실제 질문은 다음과 같습니다.

포인터가 포인터를 사용하는 경우는 무엇입니까?

포인터에 대한 포인터는 T 유형의 배열이 있을 때 나타나는 경향이 있고 T 자체는 다른 것에 대한 포인터입니다.예를들면,

  • C의 문자열은 무엇입니까?건입니다.char *.
  • 가끔 여러 줄의 줄을 원하십니까?그럼요.
  • 하나를 어떻게 선언하시겠습니까?char *x[10]:x는 10입니다에 입니다.char, 열 줄이라고도 하죠

되면 에서가 char **들어옵니다. .그것은 C에서 포인터 산술과 배열 사이의 매우 밀접한 관계에서 그림으로 들어갑니다.름,x는 항상 첫 번째 요소의 포인터로 변환됩니다.

  • 첫번째 요소는 무엇입니까? A.char *.
  • 첫번째 요소의 포인터가 뭐죠?char **.

C C ,E1[E2]됩니다와 됩니다.*(E1 + E2) . ,E1 예를 입니다라고 합시다.x되는, 으로 로됩니다.char **,그리고.E2예를 들어 3과 같은 어떤 지수입니다. (이 규칙은 또한 이유를 설명합니다.3[x]그리고.x[3]동일한 것입니다.)

포인터에 대한 포인터는 어떤 유형의 동적으로 할당된 배열을 원할 때도 표시되며, 그 배열 자체가 포인터입니다.우선 T형이 뭔지 모르는 걸로 해요.

  • 동적으로 할당된 T의 벡터를 원한다면 어떤 타입이 필요합니까?T *vec.
  • 할 수 죠는 ? 왜냐하면 우리는 C에서 포인터 연산을 할 수 있기 때문입니다.T *는 다 가 될 수 .T기억에 남습니다
  • , 와 이 를 어떻게 합니까?n요소?vec = malloc(n * sizeof(T));

이 유형의 입니다이든 입니다.T, 그래서 그것은 사실입니다.char *.

  • 종류의 입니까vec한다면T이다.char *?char **vec.

포인터에 대한 포인터는 유형 T의 인수 자체를 수정해야 하는 함수가 있을 때도 나타납니다.

  • .strtol:long strtol(char *s, char **endp, int b).
  • 이게 다 무슨 일이야?strtol합니다에서 합니다.b정수까지 그 요.그 끈이 얼마나 깊은지 알려주고 싶어합니다.를 둘 다할 수도 .long그리고.char *건 아닙니다
  • 대신 반환하기 전에 수정한 문자열의 주소를 전달하여 두 번째 결과를 반환합니다.
  • 끈이 또 뭐죠?래,오.char *.
  • 그래서 문자열의 주소는 무엇입니까?char **.

을 충분히 오래 의 도 수 있습니다.T ***types,다를 피하기 할 수 .

마지막으로 포인터에 대한 포인터는 링크된 목록의 특정 까다로운 구현에 나타납니다.C의 이중 연결 목록의 표준 선언을 생각해 보십시오.

struct node {
    struct node *next;
    struct node *prev;
    /* ... */
} *head;

여기서 삽입/삭제 기능을 재현하지는 않겠지만 약간의 문제가 있습니다.모든 노드는 목록의 머리 부분을 참조하지 않고 목록에서 제거할 수 있습니다(또는 그 이전에 새 노드를 삽입할 수도 있습니다).음, 어떤 마디도 아닙니다.이는 목록의 첫 번째 요소에 해당하지 않습니다.prev입니다.이것은 당신이 목록을 개념으로 사용하는 것보다 노드 자체와 더 많이 작업하는 어떤 종류의 C 코드에서는 적당히 짜증날 수 있습니다.이는 낮은 수준의 시스템 코드에서 비교적 흔하게 발생하는 현상입니다.

요를 다시 요?node다음과 같이:

struct node {
    struct node *next;
    struct node **prevp;
    /* ... */
} *head;

에서,prevp이 아니라 이전 다에 점입니다.next포인팅합니다.첫번째 노드는?prevp에서의 점.head. 이와 같은 목록을 그리면(이 목록이 어떻게 작동하는지 이해하기 위해 이 목록을 그려야 ) 첫 번째 요소를 제거하거나 첫 번째 요소 앞에 새 노드를 삽입할 수 있습니다.head

x: 함수 반환 포인터를 배열[]로 되돌리는 함수 반환 char" - 응?

기능이 있습니다.

이 함수는 포인터를 반환합니다.

포인터가 배열을 가리킵니다.

해당 배열은 함수 포인터(또는 함수에 대한 포인터)의 배열입니다.

이 함수들은 char*를 반환합니다.

 what's the use case for a pointer to a pointer?

하나는 인수를 통해 반환 가치를 촉진하는 것입니다.

당신이 가지고 있다고 가정해 보겠습니다.

int function(int *p)
  *p = 123;
   return 0; //success !
}

당신은 그것을 이렇게 부릅니다.

int x;
function(&x);

를 .functiony을 할 수 x우리는 그것을 우리의 x에 포인터를 보내야 합니다.

에.x라 a였습니다.char * 해요 음, 그래도 똑같네요, 우리는 그것에 대한 포인터를 넘겨야 합니다.포인터에 대한 포인터:

int function(char **p)
  *p = "Hello";
   return 0; //success !
}

당신은 그것을 이렇게 부릅니다.

char *x;
function(&x); 

읽기 기능에 대한 Remo.D의 답변은 좋은 제안입니다.다음은 다른 사람들에 대한 몇 가지 답변입니다.

포인터를 수정할 함수에 포인터를 전달하고자 하는 경우가 하나 있습니다.예를 들어,

void foo(char **str, int len)
{
   *str = malloc(len);
}

또한 문자열의 배열일 수도 있습니다.

void bar(char **strarray, int num)
{
   int i;
   for (i = 0; i < num; i++)
     printf("%s\n", strarray[i]);
}

일반적으로 이렇게 복잡한 선언문을 사용해서는 되지만 함수 포인터 같은 경우에는 상당히 복잡한 유형이 필요할 때도 있습니다.이러한 경우 중간 유형에 대해 유형 디프를 사용하는 것이 훨씬 더 읽기 쉽습니다. 예를 들어 다음과 같습니다.

typedef void foofun(char**, int);
foofun *foofunptr;

또는 "함수에 포인터를 배열[]로 되돌리는 함수"의 첫 번째 예에 대해 다음과 같은 작업을 수행할 수 있습니다.

typedef char fun_returning_char();
typedef fun_returning_char *ptr_to_fun;
typedef ptr_to_fun array_of_ptrs_to_fun[];
typedef array_of_ptrs_to_fun *ptr_to_array;
ptr_to_array myfun() { /*...*/ }

만약 인 어떤 것들은 을 가질 를 들어, 이러한 하는 함수일 수 . , , 입니다, 은(의) .fun_returning_char그럴 수도 있겠지요name_generator_type,그리고.array_of_ptrs_to_fun그럴 수도 있겠지요name_generator_list 두 두의 디프만 할 수 이 두 유형의 디프는 곳에서 될 수 있습니다 그래서 몇 줄을 접을 수도 있고, 두 가지 종류의 디프만 정의할 수도 있습니다. 다른 곳에서는 어떤 경우에도 유용할 것입니다.

char far *far *ptr;

이것은 MS-DOS와 윈도우 초기로 거슬러 올라가는 구식의 마이크로소프트 형태입니다.SHORT 버전은 64K 데이터 세그먼트의 어느 곳이나 가리키는 근거리 포인터와 달리, 이는 문자의 먼 포인터를 가리키는 먼 포인터라는 것입니다.완전히 뇌사 상태에 빠진 인텔 80x86 세그먼트 메모리 아키텍처를 해결하기 위한 마이크로소프트 메모리 모델에 대한 자세한 내용은 알고 싶지 않을 것입니다.

typedef void (*pfun)(int,float);

이것은 pfun을 int와 float을 취하는 프로시저에 대한 포인터의 typedef로 선언합니다.이것은 보통 함수 선언이나 프로토타입인 viz에 사용됩니다.

float foo_meister(pfun rabbitfun)
{
  rabbitfun(69, 2.47);
}

우리는 모든 포인터 선언문을 왼쪽에서 오른쪽으로 평가해야 합니다. 그 문장에서 포인터 이름이나 선언 이름이 선언된 부분부터 시작해서 말이죠.

선언문을 평가하면서 가장 안쪽 괄호부터 시작해야 합니다.

포인터 이름 또는 함수 이름으로 시작하여 괄호 안의 맨 오른쪽 문자 다음에 왼쪽 문자가 이어집니다.

예:

char (*(*f())[])();
         ^

char (*(*f())[])();
         ^^^
In here f is a function name, so we have to start from that.

f.

char (*(*f())[])();
            ^
Here there are no declarations on the righthand side of the current
parenthesis, we do have to move to the lefthand side and take *:

char (*(*f())[])();
      ^
f() *

우리는 내부 괄호 문자를 완성했고, 이제 우리는 이 단계 뒤의 한 단계로 돌아가야 합니다.

char (*(*f())[])();

   ------

현재 괄호의 오른쪽에 있으므로 []를 사용합니다.

char (*(*f())[])();
             ^^

f() * []

오른쪽에 문자가 없으므로 *를 사용합니다.

char (*(*f())[])();
               ^

char (*(*f())[])();
      ^
f() * [] *

char (*(*f())[])();

다음으로 바깥쪽 열기 및 닫기 괄호를 평가합니다. 함수를 나타냅니다.

f() * [] * ()

char (*(*f())[])();

이제 문 끝에 데이터 유형을 추가할 수 있습니다.

f() * [] * () char.

char (*(*f())[])();

최종 답변:

    f() * [] * () char.

f는 함수 반환 char에 대한 포인터의 배열[]에 대한 포인터를 반환하는 함수입니다.

1번과 2번은 잊어버리세요. 이것은 단지 이론적인 것입니다.

3: 됩니다에서 합니다.int main(int argc, char** argv). 수 .char** 첫 두 번째 , ...argv[0] , argv[1] , ...

함수에 인수로 포인터를 전달하면 함수가 가리키는 변수의 내용을 변경할 수 있으므로 함수 반환 값 이외의 방법으로 정보를 반환하는 데 유용할 수 있습니다.예를 들어 반환 값이 이미 오류/성공을 나타내는 데 사용되었거나 여러 값을 반환할 수 있습니다.호출 코드에서 이에 대한 구문은 foo(&var)이며 var의 주소, 즉 var에 대한 포인터를 사용합니다.

따라서 함수의 내용을 변경할 변수 자체가 포인터(예: 문자열)인 경우 매개 변수는 포인터에 대한 포인터로 선언됩니다.

#include <stdio.h>

char *some_defined_string = "Hello, " ; 
char *alloc_string() { return "World" ; } //pretend that it's dynamically allocated

int point_me_to_the_strings(char **str1, char **str2, char **str3)
{
    *str1 = some_defined_string ;
    *str2 = alloc_string() ;
    *str3 = "!!" ;

    if (str2 != 0) {
        return 0 ; //successful
    } else {
        return -1 ; //error
    }
}

main()
{
    char *s1 ; //uninitialized
    char *s2 ;
    char *s3 ;

    int success = point_me_to_the_strings(&s1, &s2, &s3) ;

    printf("%s%s%s", s1, s2, s3) ;
}

main()은 문자열에 대한 저장소를 할당하지 않으므로 point_me_to_the_strings()는 str1, str2 및 str3에 문자를 포인터로 전달하는 것처럼 쓰지 않습니다.오히려 point_me_to_the_strings()는 포인터 자체를 변경하여 다른 위치를 가리키게 하고, 포인터에 대한 포인터가 있기 때문에 이렇게 할 수 있습니다.

웹상에서 발견한 거의 모든 설명이 혼란을 해소하지 못했기 때문에 선언에 대한 생각을 추가해야겠다고 생각했습니다.

가장 먼저 해야 할 일은 선언자에서 지정자 순서를 분리하는 것입니다.

지정자 시퀀스에는 다음과 같은 키워드만 포함됩니다.const,volatile,inline과 , 와 .int,char,unsigned long. .. 절대 괄호를 포함하지 않을 것입니다.( ), [ ], *, 아니면 이름을 신고할 수도 있습니다.

선언자 OTOH는 항상 선언될 엔티티의 (사용자가 제공한) 이름을 포함하며, 선택적으로 괄호와 같은 다른 기호로 둘러싸여 있습니다.( ), [ ], *s. 일반화된 변수, 배열, 포인터 또는 함수를 지칭할 때 엔티티라는 단어를 사용하고 있습니다.

지정자 시퀀스의 유형 지정 키워드는 선언에서 명명된 것(선언된 개체)을 참조하지 않습니다.이들은 선언자결과지정된 개체를 가리킵니다.

아래는 예시입니다.

const char *names[10];

헤어졌어요...

 const char  *names[10]  ;
 ^^^^^^^^^^  ^^^^^^^^^^  
|-spec-seq-||-declrtor-|

const char선언된 엔티티를 참조하지 않습니다. names. 선언자결과지정된 엔티티를 가리킵니다. *names[10].

지정된 엔티티 유형에서 선언된 엔티티 유형을 얻기 위해 선언된 이름에서 시작하여 뒤로 작업합니다.작업 순서에 따라 별표 앞에 인접한 괄호를 읽습니다.names[10]해야 한다고 .names그리고 0과 9사이의 오프셋에서 일부 요소를 선택합니다.*names[10]선택한 요소를 삭제한다고 합니다.

먼저 0과 9 사이의 오프셋에서 원소를 선택하고, 두 번째로 방금 선택한 것을 역참조하기 때문에 다음과 같이 결정했습니다.names지정한 엔터티 유형에 대한 포인터를 포함하는 길이 10의 배열이어야 합니다.

이 됩니다(으)로 됩니다.const char.

이 내용을 종합하면, 선언문은 ...로 읽혀져야 합니다.

합니다.names열에 배열 10 10)const char.

그 대신에 우리가...

const char (*names)[10];

연산 순서를 보면 괄호 안에 있는 것을 먼저 한다고 되어 있어서 그래서.(*names)[10]먼저, 역참조라고 말합니다.names, 둘째, 0과 9 사이의 간격띄우기에서 요소를 선택합니다.

.names이제 지정된 엔티티의 배열에 대한 포인터입니다.다입니다.const char 을...라고.라고 읽었습니다.

합니다.names열의 이 10)const char.

이제 우리가...

const char *const names[10];

는.const별표 바로 뒤에 나타날 때 선언자 내부에 나타날 수 있습니다.,입니다,즉에서되는 메모리 하다는 뜻입니다.상수 포인터를 선언하는 방법입니다.그 선언문에는 ...라고 적혀 있습니다.

합니다.names열 배열(10)로서 10)const시에 대한 사항const char.

이제 이 변수는 10개의 문자열 리터럴 목록으로 초기화해야 합니다.

이제 함수 선언을 고려해 보겠습니다.

int foo(const char **);

함수 선언은 익명의 선언이기는 하지만, 인수 목록에 있는 각 인수가 다른 선언이라는 점에서 변수 선언과 다릅니다.

먼저 논쟁을 알아보겠습니다.

const char **

선언문은 익명이기 때문에, 더 잘 이해하기 위해서는 이름이 보통 있을 법한 곳에 정신적으로 무언가를 삽입해야 합니다.

const char **_anon_

만약 우리가 언급을 취소한다면_anon_지정된 개체를 두 번 받으면 됩니다.입니다 입니다.const char일 수 이름의 배열일 수 있습니다.

이제 완전한 선언자를 생각해보세요...

foo(const char **);

입니다.foo를 합니다"에합니다.foo", "를 합니다.foo는 함수입니다.입니다에서 입니다.foo.

그러므로...

int foo(const char **);

...라고 읽힐 수 있습니다.

합니다.foo로서 로서 터]합니다에 를 표시하는]합니다.const char 및 ]]int.

이제 생각해보세요...

int (*foo)(const char **);

는.(*foo)(const char **)먼저, 역참조라고 말합니다.foo, 둘째, 포인터에 포인터를 입력할 유형 - 포인터의 인수를 전달합니다.const char

.foo논의가 전달되기 에 기각되어야 합니다.foo는 이제 함수의 포인터입니다.따라서 그 선언은...

합니다.foo로서 서에 합니다]로에 대한 포인터의 인수를 const char 및 ]]int.

이제 괄호가 없는 경우를 생각해 보십시오.

int *foo(const char **);

작업 순서가 다르기 때문에 의미가 완전히 바뀝니다.이제 먼저 포인터를 가리키는 유형 - 포인터의 인수를 전달합니다.const char~에게foo, 그런 다음, 반환 값을 역참조하여 다음을 구합니다.int.

은 을 의미합니다.foo는 함수에 대한 포인터가 아니라 함수에 대한 포인터를 반환하는 함수입니다.int 완전한 와 같이 되어 있습니다 따라서 완전한 선언은...

합니다.foo터]합니다에 를 표시하는] 합니다.const char 및 터로 를및합니다. [int합니다]합니다.

그럼 이건...

int (*foo(const char **))();

이전과 마찬가지로, 논쟁은 다음으로 넘어갔습니다.foo투 투는"낼 포인터.const char가 ". 하십시오"에 할 때 foo다를 .int은 . .int은 .foo더 .int 를 , .int.

하자면,int (*foo(const char **))(double)는 함수 포인터를 반환하는 함수의 선언입니다.그 선언은 ...로 읽힙니다.

합니다.foo로서 로서 터]합니다에 를 표시하는]합니다.const char및[ . ]및 returning type [환 type int.

이제 내가 생각해낼 수 있는 가장 끔찍한 혐오스러운 선언입니다.이 IRL과 같은 것은 절대 볼 수 없을 것이지만 연습을 위해 읽을 수 있다면 거의 모든 것을 읽을 수 있을 것입니다.

const char *const (*foo(int *[4], int (*)(const char *)))(double *(*)[]);

제정신을 유지하려면, 우리는 이것을 부분적으로 쪼개서 내부에서부터 시작해야 합니다.

먼저 밑줄 친 이 문구를 단독으로 보세요.

const char *const (*foo(int *[4], int (*)(const char *)))(double *(*)[]);
                        ^^^^^^^^

이 .*고.[은, 선언입니다.int *_anon_[4]3를 . 가 0 3 에서 ._anon_ 하면,int 친 읽습니다 그래서 밑줄 친 문구를 다음과 같이 읽습니다.

에 4)int.

이제 밑줄 친 이 문구를 생각해 보세요.

const char *const (*foo(int *[4], int (*)(const char *)))(double *(*)[]);
                                  ^^^^^^^^^^^^^^^^^^^^^

.*인에(*)에 에도 말입니다*인에(const char*) 먼저 안쪽 문구를 읽어보도록 하겠습니다.

const char *_anon_
                  

_anon_을 주다const char 말이야, ㅇconst char * 냥".const char 전체 친 ." 이제 전체 밑줄 친 문구로 넘어가겠습니다.

int (*_anon_)(const char *)

만약 우리가 언급을 취소한다면_anon_로, 포인터 합니다"를 전달합니다.const char으로서, 을 됩니다.int "..."라는 가 있습니다.int (*)(const char*)읽다...

[에 [에]const char.]int

이제 하나 더 내면의 문구.

const char *const (*foo(int *[4], int (*)(const char *)))(double *(*)[]);
                                                          ^^^^^^^^^^^^^        

은 에 합니다.double *(*_anon_)[]는 참조를 취소합니다._anon_그런 으로 해당 를 다 됩니다.double가.double *(*)[]이렇게 읽힙니다...

() 를 double

이제 전체 선언을 다시 생각해 보십시오.

const char *const (*foo(int *[4], int (*)(const char *)))(double *(*)[]);
           

단순화하기 위해 이미 해독한 구문을 레이블로 바꿉니다.

const char *const (*foo([...1...],[...2...]))([...3...]);

가 논쟁을 한다면 한다면.[...1...]그리고.[...2...].foo로 전달한 [...3...] 의 수 .const char.

이것은 완전한 선언이...

합니다.foo및 [...3...] 인수를 로서 [...1...], [...2...]고 [...3...]고 []합니다를 합니다.constconst char] ].

아니면...

합니다.foo열 기합니다 4)]형에 4.int [ [ , 및에 대한 ] 용] [const char.]int크기)], [수에 ]], [터]] [기환)]][[함수 ]]로를 반환합니다.double는로 constconst char] ].

그렇게foo에 대한 .int함수 포인터를 인수로 지정하고 다른 함수 포인터를 반환합니다.두 함수 포인터의 인수와 반환 값을 지정해야 하는 필요성은 선언을 엄청나게 복잡하게 만듭니다.

언급URL : https://stackoverflow.com/questions/1448849/how-do-i-understand-complicated-function-declarations