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


188

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

일부 C 코드

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

흥미로운 설명

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

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


9
설명이 어떻게 더 간단해질 수 있습니까? 각 핵심 요점에 대한 fw / 두 단어의 정의의 모든 측면을 다룹니다.
Scott Hunter

30
아마도 C는 어려울까요? 그것은 실제로 f어떤 인수를 취할 수있는 함수에 대한 포인터 배열로 선언합니다 . 만약 void (*(*f[])(void))(void);그렇다면,
인수가

18
실제로는 이러한 모호한 코드를 코딩하지 마십시오. 서명에 typedef 사용
Basile Starynkevitch

4
함수 포인터와 관련된 선언은 어려울 수 있습니다. 그렇다고 일반 C 또는 C ++가 같은 방식으로 어렵다는 의미는 아닙니다. 다른 언어에서는 함수 포인터가없는 것을 포함하여 여러 가지 방법으로이 문제를 해결합니다. 경우에 따라 중요한 부분을 생략 할 수도 있습니다.
Kate Gregory

20
눈을 가늘게 뜨면 LISP처럼 보입니다.
user2023861

답변:


117

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

에서 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 코멘트에).


5
cdecl.org가 더 나았으면 좋겠습니다
Grady Player

8
"나선형 규칙"은 없습니다 ... "int *** foo [] [] []"는 포인터에 대한 포인터 배열의 배열을 정의합니다. "나선형"은이 선언이 괄호 안에있는 것들을 번갈아 가며 묶어 놓았다는 사실에서만 나옵니다. 각 괄호 안의 오른쪽에있는 다음 왼쪽에있는 모든 것입니다.
Random832

1
@ Random832 "나선형 규칙"이 있으며 방금 언급 한 사례, 즉 괄호 / 배열 등을 처리하는 방법에 대해 설명합니다. 물론 표준 C 규칙은 아니지만 처리 방법을 파악하기위한 좋은 니모닉 복잡한 선언으로. IMHO, 그것은 매우 유용하고 문제가 있거나 cdecl.org 가 선언을 파싱 할 수 없을 때 당신을 저장합니다 . 물론 그러한 선언을 남용해서는 안되지만 구문 분석 방식을 아는 것이 좋습니다.
vsoftco

5
@vsoftco 그러나 괄호에 도달했을 때만 돌아 가면 "나선형 / 시계 방향으로 움직이지 않습니다".
Random832

2
ouah, 당신은 나선형 규칙이 보편적이지 않다는 것을 언급해야합니다 .
haccks

105

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

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])())();어느 함수도 인수를 사용하지 않는다고 가정).


7
'신호'의 예를 들어 주셔서 감사합니다. 이러한 종류의 것들이 야생에 나타남을 보여줍니다.
Justsalt

좋은 예입니다.
Casey

나는 당신의 f감속 트리가 우선 순위를 설명하는 것을 좋아했습니다 ... 어떤 이유로 든 특히 ASCII 설명에서 킥을 얻습니다. 특히 설명 할 때 :)
txtechhelp

1
두 함수 모두 인수 를 사용 하지 않는다고 가정하면void 함수 괄호로 묶어야합니다.
haccks

1
@haccks : 선언을 위해, 네; 함수 호출에 대해 이야기하고있었습니다.
John Bode

57

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

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

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

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

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

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

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

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

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

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


3
내가 본 적이없는 그것을 보는 좋은 방법. +1
tbodt

4
좋은. 이 방법으로 보면 정말 간단 합니다. 실제로 s vector< function<function<void()>()>* > f를 추가하는 경우 와 같은 것보다 실제로 더 쉽습니다 std::. (그러나 글쎄, 예제 고안되었다. 심지어 f :: [IORef (IO (IO ()))]이상해 보인다.)
leftaroundabout

1
@TimoDenk : 선언 a[x]a[i]때 표현식 이 유효 함을 나타냅니다 i >= 0 && i < x. 반면, a[]크기를 지정하지 않은 상태로두면 다음과 동일합니다 *a. 이는 표현식이 a[i](또는 동등하게 *(a + i)) 일부 범위에 유효 함을 나타냅니다 i.
Jon Purdy

4
이것은 C 유형에 대해 생각하는 가장 쉬운 방법입니다. 감사합니다
Alex Ozer

4
나는 이것을 좋아한다! 바보 같은 나선보다 추론하기가 훨씬 쉽습니다. (*f[])()인덱싱하고 역 참조한 다음 호출 할 수있는 유형이므로 함수에 대한 포인터 배열입니다.
Lynn

32

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

나선형 규칙을 적용하거나 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 책에서 발췌 한 것입니다 .


이것은 표준의 접근 방식, 즉 "선언 미러 사용법"과 같습니다. 이 시점에서 다른 질문을하고 싶습니다 : KN King의 책을 제안 하시겠습니까? 책에 대한 좋은 리뷰가 많이 있습니다.
Motun

1
네. 나는 그 책을 제안한다. 나는 그 책에서 프로그래밍을 시작했다. 좋은 글과 거기에 문제가 있습니다.
haccks

선언을 이해하지 못하는 cdecl의 예를 제공 할 수 있습니까? cdecl은 컴파일러와 동일한 구문 분석 규칙을 사용한다고 생각했으며 항상 작동한다고 말할 수 있습니다.
Fabio는 Reinstate Monica

@FabioTurati; 함수는 배열이나 함수를 반환 할 수 없습니다. char (x())[5]구문 오류가 발생해야하지만, 같은 파싱 CDECL : 선언 x함수의 어레이 (5) 등을 반환char .
haccks

12

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


음, "나선형 방식"에, 당신은 오른쪽 당신은 당신이 수 후까지 등, 왼쪽하지만 종종 ... 잘못 설명 된 것, 할 수있는만큼 이동

7

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


3
그럼에도 불구하고 typedef를 구문 분석하는 방법 만 알더라도 구문 분석 방법을 아는 것이 중요합니다!
inetknght

1
@inetknght, typedef로 수행하는 방법은 구문 분석이 필요하지 않도록 충분히 간단하게 만드는 것입니다.
SergeyA

2
인터뷰 중에 이러한 유형의 질문을하는 사람들은 자아를 쓰다듬기만합니다.
Casey

1
@JohnBode, 함수의 반환 값을 typedefing하여 자신에게 호의를 베풀 것입니다.
SergeyA

1
@JohnBode, 나는 토론 할 가치가없는 개인적인 선택의 문제를 발견했다. 나는 당신의 선호를보고, 나는 여전히 내 것을 가지고 있습니다.
SergeyA

7

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

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


1
이 단어는 내부 에 Parens 또는 한 줄로 중첩 된 것으로 표시되지 않습니다 . LTR 라인 다음에 RTL 라인이있는 "스네이크"패턴을 설명합니다.
Potatoswatter

5

이것의 유용성과 관련하여 쉘 코드로 작업 할 때이 구성이 많이 보입니다.

int (*ret)() = (int(*)())code;
ret();

문법적으로 복잡하지는 않지만이 특정 패턴이 많이 나옵니다.

SO 질문 에서 더 완전한 예 .

따라서 원래 그림의 범위에 대한 유용성은 의문의 여지가 있지만 (제작 코드를 크게 단순화해야한다고 제안하지만) 약간의 구문 구조가 있습니다.


5

선언

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

모호한 말입니다

Function f[]

typedef void (*ResultFunction)();

typedef ResultFunction (*Function)();

실제로는 ResultFunctionFunction 대신 더 설명적인 이름이 필요합니다 . 가능하면 매개 변수 목록을로 지정하십시오 void.


4

Bruce Eckel이 설명한 방법이 도움이되고 따르기 쉽다는 것을 알았습니다.

함수 포인터 정의

인수가없고 반환 값이없는 함수에 대한 포인터를 정의하려면 다음과 같이 말합니다.

void (*funcPtr)();

이와 같은 복잡한 정의를 볼 때 공격하는 가장 좋은 방법은 중간에서 시작하여 나가는 것입니다. "중간에서 시작"은 변수 이름 (funcPtr)에서 시작하는 것을 의미합니다. "나가기"는 가장 가까운 항목 (이 경우에는 아무 것도 괄호로 묶이지 않음)을 오른쪽으로 찾은 다음 왼쪽 (별표로 표시된 포인터)을 찾은 다음 오른쪽 ( 인수를 사용하지 않는 함수를 나타내는 빈 인수 목록) 왼쪽을 봅니다 (void, 함수에 반환 값이 없음을 나타냄). 이 오른쪽-왼쪽-오른쪽 동작은 대부분의 선언에서 작동합니다.

"중간에서 시작"( "funcPtr은 ..."임)을 검토하려면 오른쪽으로 이동하십시오 (아무것도 없습니다 – 오른쪽 괄호로 멈춤). 왼쪽으로 이동하여 '*'(“ ... ...에 대한 포인터) 오른쪽으로 이동하여 빈 인수 목록 ( "... 인수가없는 함수 ...")을 찾은 다음 왼쪽으로 이동하여 공백을 찾습니다 ( "funcPtr은 인수를 취하지 않고 void를 반환하는 함수에 대한 포인터”).

* funcPtr에 괄호가 필요한 이유가 궁금 할 수 있습니다. 그것들을 사용하지 않으면 컴파일러는 다음을 볼 것입니다 :

void *funcPtr();

변수를 정의하는 대신 함수 (void *를 반환하는)를 선언합니다. 컴파일러는 선언이나 정의가 무엇인지 알아낼 때와 동일한 프로세스를 거치는 것으로 생각할 수 있습니다. 이 괄호는“충돌”해야하므로 왼쪽으로 돌아가서 오른쪽으로 계속해서 빈 인수 목록을 찾는 대신 '*'를 찾습니다.

복잡한 선언 및 정의

또한 C 및 C ++ 선언 구문의 작동 방식을 파악하면 훨씬 복잡한 항목을 만들 수 있습니다. 예를 들어 :

//: C03:ComplicatedDefinitions.cpp

/* 1. */     void * (*(*fp1)(int))[10];

/* 2. */     float (*(*fp2)(int,int,float))(int);

/* 3. */     typedef double (*(*(*fp3)())[10])();
             fp3 a;

/* 4. */     int (*(*f4())[10])();


int main() {} ///:~ 

각각을 살펴보고 오른쪽 왼쪽 지침을 사용하여 파악하십시오. 숫자 1 은 "fp1은 정수 인수를 사용하여 10 개의 void 포인터 배열에 대한 포인터를 반환하는 함수에 대한 포인터입니다"라고 말합니다.

숫자 2 는 "fp2는 세 개의 인수 (int, int 및 float)를 취하는 함수에 대한 포인터이며 정수 인수를 가져 와서 float를 반환하는 함수에 대한 포인터를 반환합니다"라고 말합니다.

복잡한 정의를 많이 작성하는 경우 typedef를 사용할 수 있습니다. 숫자 3 은 typedef가 매번 복잡한 설명을 입력하는 방법을 보여줍니다. "fp3은 인수를 취하지 않는 함수에 대한 포인터이며 인수를 취하지 않고 복식을 리턴하는 함수에 대한 10 개의 포인터 배열에 대한 포인터를 리턴합니다." 그런 다음 "a는 이러한 fp3 유형 중 하나입니다."라고 말합니다. typedef는 일반적으로 간단한 설명에서 복잡한 설명을 작성하는 데 유용합니다.

숫자 4 는 변수 정의 대신 함수 선언입니다. "f4는 정수를 반환하는 함수에 대한 10 개의 포인터 배열에 대한 포인터를 반환하는 함수입니다."

이와 같이 복잡한 선언과 정의가 필요한 경우는 거의 없습니다. 그러나, 당신이 그것들을 알아내는 운동을한다면, 당신은 당신이 실제 생활에서 접할 수있는 약간 복잡한 것들로 약간 방해받지 않을 것입니다.

출처 : C ++ Volume 1, 2 판, 3 장, Bruce Eckel의 "함수 주소"부분에서 생각하기.


4

C 선언에 대한 다음 규칙을 기억하십시오.
우선 순위는 의심의 여지가 없습니다.
접미사로 시작하고 접두사로 진행
하며 내부에서 두 세트를 모두 읽습니다.
-1980 년대 중반

물론 괄호로 수정 된 것을 제외하고. 그리고 이것을 선언하는 구문은 해당 변수를 사용하여 기본 클래스의 인스턴스를 얻는 구문을 정확하게 반영합니다.

진지하게, 이것은 한 눈에 배우는 것이 어렵지 않습니다. 당신은 단지 기술을 연습하는데 시간을 할애해야합니다. 당신은 유지하거나 다른 사람이 작성한 C 코드를 적응하는 거라면, 그건 확실히 시간이 가치 투자가. 또한 그것을 배우지 않은 다른 프로그래머들을 놀라게하는 재미있는 파티 트릭입니다.

자신의 코드 : 항상 그렇듯이 무언가 하나의 라이너로 작성 될 있다는 사실이 표준 관용구가 된 매우 일반적인 패턴 (예 : 문자열 복사 루프)이 아니라면 반드시 그렇게해야한다는 의미는 아닙니다. . 당신과 당신을 따르는 사람들은 "한 번에 한 번에"이들을 생성하고 구문 분석 할 수있는 능력에 의존하기보다는 계층화 된 typedef와 단계별 역 참조로 복잡한 유형을 만들면 훨씬 더 행복 할 것입니다. 성능도 좋아지고 코드 가독성과 유지 관리 성이 훨씬 향상됩니다.

더 나빠질 수 있습니다. 다음과 같이 시작되는 합법적 인 PL / I 선언이있었습니다.

if if if = then then then = else else else = if then ...

2
PL / I 문은 IF IF = THEN THEN THEN = ELSE ELSE ELSE = ENDIF ENDIF로 해석되고 구문 분석됩니다 if (IF == THEN) then (THEN = ELSE) else (ELSE = ENDIF).
Cole Johnson

내가 생각하는 C의 동등한 조건 IF / THEN / ELSE 표현 (? :) 믹스에 3 세트를 가지고 사용하여 한 단계 더를했다 버전이 있었다 ...하지만 그것은 수십 년 지났는데있을 수 있습니다 언어의 특정 방언에 의존합니다. 요점은 어떤 언어라도 적어도 하나의 병리학 적 형태를 가지고 있다는 점입니다.
keshlam

4

나는 몇 년 전 (많은 머리카락이 있었을 때) 오를 쓴 나선형 규칙의 원래 저자가되었고, 그것이 cfaq에 추가되었을 때 영광이었습니다.

나는 학생들과 동료들이 "그들의 머리 속에"C 선언을보다 쉽게 ​​읽을 수 있도록 나선형 규칙을 썼다. 즉, cdecl.org 등과 같은 소프트웨어 도구를 사용할 필요가 없습니다. 나선 규칙이 C 표현식을 구문 분석하는 정식 방법이라고 선언하는 것은 결코 제가 의도하지 않았습니다. 하지만이 규칙이 문자 그대로 수천 명의 C 프로그래밍 학생과 실무자에게 도움이되었다는 것을 알게되어 기쁩니다.

기록을 위해

Linus Torvalds (내가 엄청나게 존경하는 사람)를 포함하여 여러 사이트에서 여러 번 "정확하게"확인되었으며, 나선 규칙이 "무너지는"상황이 있습니다. 가장 일반적인 것 :

char *ar[10][10];

이 스레드의 다른 사람들이 지적했듯이 배열을 만날 때 다음과 같이 작성된 것처럼 모든 인덱스 사용하도록 규칙을 업데이트 할 수 있습니다 .

char *(ar[10][10]);

이제 나선형 규칙에 따라 얻을 수 있습니다.

"ar는 char에 대한 포인터의 10x10 2 차원 배열입니다"

나선 법칙이 C를 배우는 데 유용성을 갖기를 바랍니다.

추신:

나는 "C는 어렵지 않다"이미지를 좋아한다 :)


3
  • (*(*f[]) ()) ()

해결 void>>

  • (*(*f[]) ()) () = 무효

수용 ()>>

  • (* (*f[]) ()) = 함수 반환 (void)

해결 *>>

  • (*f[]) () = (에 대한 함수 반환 (void))에 대한 포인터

해결 ()>>

  • (* f[]) = 함수 반환 (포인터 반환 (void))

해결 *>>

  • f[] = 포인터를 가리키는 (함수를 반환하는 포인터 ((함수를 반환하는 함수)))

해결 [ ]>>

  • f = (포인터-(함수 반환 (포인터 ((void)))))의 배열
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.