[c] C는 그렇게 어렵지 않습니다 : void (* (* f []) ()) ()

방금 오늘 사진을 보았고 설명을 부탁드립니다. 여기 그림이 있습니다 :

일부 C 코드

나는 이것이 혼란스럽고 그러한 코드가 실제로 실용적인지 궁금해했다. 나는 사진을 googled 하고이 reddit 항목 에서 다른 사진을 발견 했으며 여기에 그 사진이 있습니다.

흥미로운 설명

그래서이 “나 선식으로 읽는”것은 무엇인가? 이것이 C 컴파일러가 구문 분석하는 방법입니까?
이 이상한 코드에 대한 간단한 설명이 있으면 좋을 것입니다.
이 외에도 이런 종류의 코드가 유용 할 수 있습니까? 그렇다면 언제 어디서?

질문 “나선형 규칙”에 대한,하지만 난 그냥 그 규칙을 읽는 방법이 적용된 방법이나 표현에 대해 물어 아니에요. 나는 그러한 표현의 사용과 나선 규칙의 타당성에 의문을 제기하고 있습니다. 이것에 관해서는, 좋은 답변이 이미 게시되어 있습니다.



답변

복잡한 선언의 의미를 찾는 데 도움이 되는 “시계 방향 / 나선 규칙” 이라는 규칙이 있습니다 .

에서 C-자주 묻는 질문 :

따라야 할 세 가지 간단한 단계가 있습니다.

  1. 미지의 요소로 시작하여 나선형 / 시계 방향으로 움직입니다. 다음 요소를 고려할 때 해당 영어 문장으로 대체하십시오.

    [X]또는 []
    => Array X size of … 또는 Array undefined size of …

    (type1, type2)
    => 함수가 type1과 type2를 전달하는 중 …

    *
    => 포인터 …

  2. 모든 토큰이 덮힐 때까지 나선형 / 시계 방향으로 계속하십시오.

  3. 항상 괄호 안의 모든 것을 먼저 해결하십시오!

예를 들어 위의 링크를 확인할 수 있습니다.

또한 다음과 같은 웹 사이트가 있습니다.

http://www.cdecl.org

C 선언을 입력하면 영어 의미를 갖습니다. 에 대한

void (*(*f[])())()

출력 :

f를 함수에 대한 포인터의 배열로 선언하십시오.

편집하다:

Random832 의 의견에서 지적했듯이 나선형 규칙은 배열 배열을 처리하지 않으며 이러한 선언에서 (대부분의) 잘못된 결과를 초래합니다. 예를 들어 int **x[1][2];, 나선형 규칙의 []경우 우선 순위가 높은 사실을 무시합니다 *.

배열 배열 앞에 나선 규칙을 적용하기 전에 먼저 명시적인 괄호를 추가 할 수 있습니다. 예를 들면 다음 int **x[1][2];과 같은 것이다 int **(x[1][2]);올바르게 인해 다음에 우선 (또한 유효 C)와 스파이럴 규칙 올바른 영어 선언은 “x는 INT 포인터 포인터의 어레이 (2)의 배열 1″로 판독한다.

이 문제는이에 포함 된 것을 참고 대답 하여 제임스 칸 세이 (지적 haccks 코멘트에).


답변

“나선형”규칙 종류는 다음 우선 순위 규칙에서 제외됩니다.

T *a[]    -- a is an array of pointer to T
T (*a)[]  -- a is a pointer to an array of T
T *f()    -- f is a function returning a pointer to T
T (*f)()  -- f is a pointer to a function returning T

첨자 []와 함수 호출 ()연산자는 단항보다 더 높은 우선 순위를 가지고 있습니다 *그래서, *f()같은 구문 분석 *(f())*a[]같은 구문 분석됩니다 *(a[]).

당신이 배열 또는 함수에 대한 포인터에 대한 포인터를 원한다면, 당신은 명시 적으로 그룹에 필요한 *에서와 같이 식별자, (*a)[]또는 (*f)().

그럼 당신은 그 실현 af단지 식별자보다 더 복잡하게 표현 될 수있다; 으로는 T (*a)[N], a단순한 식별되거나, 그와 같은 함수를 호출 할 수있는 (*f())[N]( a-> f()), 또는이 같은 배열 될 수있다 (*p[M])[N]( a-> p[M]), 또는이 같은 함수 포인터 배열 될 수있다 (*(*p[M])())[N]( a-> (*p[M])()) 기타

간접 연산자 *가 단항 대신 접두어 인 경우 좋을 것이므로 선언을 왼쪽에서 오른쪽으로 읽는 것이 다소 쉬워집니다 ( void f[]*()*();확실히보다 낫습니다 void (*(*f[])())()).

이와 같은 털이 선언을 발견하면 가장 왼쪽의 식별자 를 찾아 위의 우선 순위 규칙을 적용하여 함수 매개 변수에 재귀 적으로 적용하십시오.

         f              -- f
         f[]            -- is an array
        *f[]            -- of pointers  ([] has higher precedence than *)
       (*f[])()         -- to functions
      *(*f[])()         -- returning pointers
     (*(*f[])())()      -- to functions
void (*(*f[])())();     -- returning void

signal표준 라이브러리 의 기능은 아마도 이런 종류의 광기에 대한 유형 표본 일 것입니다.

       signal                                       -- signal
       signal(                          )           -- is a function with parameters
       signal(    sig,                  )           --    sig
       signal(int sig,                  )           --    which is an int and
       signal(int sig,        func      )           --    func
       signal(int sig,       *func      )           --    which is a pointer
       signal(int sig,      (*func)(int))           --    to a function taking an int                                           
       signal(int sig, void (*func)(int))           --    returning void
      *signal(int sig, void (*func)(int))           -- returning a pointer
     (*signal(int sig, void (*func)(int)))(int)     -- to a function taking an int
void (*signal(int sig, void (*func)(int)))(int);    -- and returning void

이 시점에서 대부분의 사람들은 “use typedefs”라고 말합니다.

typedef void outerfunc(void);
typedef outerfunc *innerfunc(void);

innerfunc *f[N];

그러나…

식에 어떻게 사용 f 하시겠습니까? 포인터 배열이라는 것을 알고 있지만 올바른 함수를 실행하는 데 어떻게 사용합니까? typedef를 살펴보고 올바른 구문을 퍼즐로 만들어야합니다. 대조적으로, “네이 키드 (naked)”버전은 눈에 띄지 않지만, 표현식에서 사용 하는 방법을 정확하게 알려줍니다 f(즉, (*(*f[i])())();어느 함수도 인수를 사용하지 않는다고 가정).


답변

C에서 선언은 사용법을 반영합니다. 이것이 표준에 정의 된 방식입니다. 선언 :

void (*(*f[])())()

표현식 (*(*f[i])())()이 유형의 결과를 생성 한다고 가정합니다 void. 다음을 의미합니다.

  • f 색인을 생성 할 수 있으므로 배열이어야합니다.

    f[i]
  • f역 참조 할 수 있으므로 의 요소는 포인터 여야합니다.

    *f[i]
  • 이러한 포인터는 인수를 사용하지 않는 함수에 대한 포인터 여야합니다.

    (*f[i])()
  • 해당 함수의 결과는 포인터이어야합니다.

    *(*f[i])()
  • 이러한 포인터는 인수를 사용하지 않는 함수에 대한 포인터 이기도 합니다.

    (*(*f[i])())()
  • 해당 함수 포인터는 void

“나선형 규칙”은 동일한 것을 이해하는 다른 방법을 제공하는 니모닉입니다.


답변

그래서이 “나 선식으로 읽는”것은 무엇인가?

나선형 규칙을 적용하거나 cdecl 을 사용 하는 것이 항상 유효한 것은 아닙니다. 어떤 경우에는 둘 다 실패합니다. 나선형 규칙은 많은 경우에 작동하지만 보편적이지 않습니다 .

복잡한 선언을 해독하려면 다음 두 가지 간단한 규칙을 기억하십시오.

  • 항상 내부에서 선언을 읽으십시오 . 가장 괄호로 시작하십시오 (있는 경우). 선언 된 식별자를 찾아서 선언을 해독하십시오.

  • 이 항상 선호, 선택의 여지 경우 []()이상* : 만약 *선행 식별자와 []그것을 다음, 식별자는 배열이 아닌 포인터를 나타냅니다. 마찬가지로, *식별자 앞에 식별자가 오면 식별자 ()는 포인터가 아니라 함수를 나타냅니다. (괄호는 항상 정상적인 우선 순위를 무시하는 데 사용할 수있는 []()이상을 *.)

이 규칙은 실제로 식별자의 한 쪽에서 다른쪽으로 지그재그 를하는 것입니다.

이제 간단한 선언을 해독

int *a[10];

적용 규칙 :

int *a[10];      "a is"  
     ^  

int *a[10];      "a is an array"  
      ^^^^ 

int *a[10];      "a is an array of pointers"
    ^

int *a[10];      "a is an array of pointers to `int`".  
^^^      

다음과 같이 복잡한 선언을 해독합시다

void ( *(*f[]) () ) ();  

위의 규칙을 적용하여 :

void ( *(*f[]) () ) ();        "f is"  
          ^  

void ( *(*f[]) () ) ();        "f is an array"  
           ^^ 

void ( *(*f[]) () ) ();        "f is an array of pointers" 
         ^    

void ( *(*f[]) () ) ();        "f is an array of pointers to function"   
               ^^     

void ( *(*f[]) () ) ();        "f is an array of pointers to function returning pointer"
       ^   

void ( *(*f[]) () ) ();        "f is an array of pointers to function returning pointer to function" 
                    ^^    

void ( *(*f[]) () ) ();        "f is an array of pointers to function returning pointer to function returning `void`"  
^^^^

다음은 진행 방법을 보여주는 GIF입니다 (더 크게 보려면 이미지를 클릭하십시오).

여기에 이미지 설명을 입력하십시오


여기에 언급 된 규칙은 KN KING의 C Programming A Modern Approach 책에서 발췌 한 것입니다 .


답변

이 선언에서는 각 괄호 안의 각 측면에 한 명의 연산자 만 있기 때문에 “나선형”입니다. “나선형”으로 진행한다고 주장하면 int ***foo[][][]실제로 모든 배열 레벨이 포인터 레벨보다 앞에 올 때 선언에서 배열과 포인터 사이를 번갈아 표시하는 것이 좋습니다 .


답변

이와 같은 구조가 실제 생활에서 사용될 수 있을지 의심됩니다. 나는 심지어 일반 개발자를위한 인터뷰 질문 (컴파일러 작성자에게는 괜찮을 수도 있음)으로 그들을 비난했다. 대신 typedef를 사용해야합니다.


답변

임의의 퀴즈 factoid으로, 당신이 C 선언을 읽는 방법에 대해 설명하는 영어의 실제 단어가 있다는 것을 알고 즐겁게 찾을 수 있습니다 Boustrophedonically 으로 오른쪽에서 왼쪽으로 번갈아이다, 왼쪽에서 오른쪽으로.

참조 : 린든 1994 데르 반페이지 76