2013-04-28 3 views
8

필자는 최근 복합 선언을 해독하는 데 대해 the spiral rule을 배웠다.이 선언은 일련의 typedef로 작성되어야한다. 그러나 다음과 같은 코멘트 나 알람 : 나는 void (*signal(int, void (*fp)(int)))(int);는 "간단한 경우"를 찾을 수없는신고에 대한 나선형 규칙 - 언제 잘못 되었습니까?

A frequently cited simplification, which only works for a few simple cases.

. 그건 좀 더 놀라운 일입니다.

제 질문은 입니다. 어떤 상황에서 규칙을 적용하고 오류가 발생할 수 있습니까?

+3

내가 갈 때'Vorac'에게 물어볼 것을 제안한다. 잘못된; 나는 그것이 잘못되는 상황을 알지 못한다. –

+0

@Jonathan Leffler, 알려진 문제가 없으므로 나선 규칙이 결코 잘못되지 않습니까? – Vorac

+1

OK - [의견]이 (가)있는 [James Kanze] (http://stackoverflow.com/users/649665/james-kanze)입니다. (http://stackoverflow.com/questions/7526152/easy-rule-to -read-complicated-const-declarations/7526298 # comment23257858_7526298) 1994 년에 게시 된 기사 (당신이 아닌, 귀인 읽기 오해에 사과드립니다.) 나는 야고보가 반대 한 것이 무엇인지 모른다. 나는 그것이 실패한 상황을 알지 못한다. (그러나 나는 열심히 공부하지 않았다.) 그러한 상황이 없다는 것과 같은 것은 아니지만, 나는 설명 할 수없는, 명백하지 않은 주석을 많은 연기로 간주합니다. –

답변

2

예컨대 :

int * a[][5]; 

int의 배열에 대한 포인터의 배열이 아닙니다.

+0

그럼이게 뭐야? – yapkm01

+0

@ yapkm01'int'에 5 개의 포인터 배열 배열입니다. –

3

규칙이 정확합니다. 그러나, 그것을 적용하는 것은 아주 조심해야한다.

C99 + 선언에보다 공식적인 방식으로 적용하는 것이 좋습니다.

여기서 가장 중요한 것은 모든 선언 (const는, volatile는, static는, extern는, inline는, structunion, typedef 단순의 사진에서 제거되지만 다시 쉽게 추가 할 수 있습니다) 다음과 같은 순환 구조를 인식하는 것입니다 :

base-type [derived-part1: *'s] [object] [derived-part2: []'s or()] 

네 부분입니다.

    [object]

은 모든 4 개 부분 구문 분석했으면

where 

    base-type is one of the following (I'm using a bit compressed notation): 
    void 
    [signed/unsigned] char 
    [signed/unsigned] short [int] 
    signed/unsigned [int] 
    [signed/unsigned] long [long] [int] 
    float 
    [long] double 
    etc 

    object is 
     an identifier 
    OR 
     ([derived-part1: *'s] [object] [derived-part2: []'s or()]) 

    * is *, denotes a reference/pointer and can be repeated 
    [] in derived-part2 denotes bracketed array dimensions and can be repeated 
() in derived-part2 denotes parenthesized function parameters delimited with ,'s 
    [] elsewhere denotes an optional part 
() elsewhere denotes parentheses 

, [derived-part2 (포함/반환)] [derived-part2 (포인터)] base-type1.

재귀가있는 경우 재귀 스택 맨 아래에 object (있는 경우)이 있음을 알 수 있습니다. 가장 내부에있는 것이므로 다시 돌아가서 수집 및 결합하여 전체 선언을 얻을 수 있습니다 재귀의 각 수준에서 파생 된 부분

구문 분석 중에 을 [derived-part2] (있는 경우)으로 이동할 수 있습니다. 이렇게하면 선형화되고 이해하기 쉬운 선언을 할 수 있습니다 (위의 참조).

따라서,

char* (**(*foo[3][5])(void))[7][9]; 

에 당신이 얻을 :

  1. base-type = char
  2. 수준 1 : = *derived-part1 = (**(*foo[3][5])(void))object, derived-part2 = [7][9]
  3. 레벨 2 : derived-part1 = ** = (*foo[3][5])object, derived-part2 = (void)
  4. 레벨 3 = *derived-part1 = fooobject, derived-part2 = [3][5]

거기서 :

  1. 레벨 3 : *[3][5]foo
  2. 수준 2 : 01 234,841,(void)*[3][5]foo
  3. 레벨 1 : 마지막으로 *[7][9]**(void)*[3][5]foo
  4. , [3][5]foo

char*[7][9]**(void)* 지금, 오른쪽에서 왼쪽으로 읽는 :

foo은 char에 대한 9 개의 포인터로 이루어진 7 개의 배열로 된 포인터에 대한 포인터에 대한 포인터를 반환하는 함수에 대한 5 개의 포인터로 이루어진 3 개의 배열로 구성된 배열입니다 (매개 변수 없음).

공정에서 각 derived-part2의 배열 크기를 역으로 사용할 수 있습니다.

그건 나선형 규칙입니다.

그리고 나선형을 쉽게 볼 수 있습니다. 왼쪽에서부터 더 깊게 중첩 된 [object]을 다이빙하고 오른편에 서페이스의 위쪽에는 왼쪽과 오른쪽의 다른 한 쌍이 있다는 점에 유의하십시오.

+0

흥미 롭습니다. 먼저 규칙이 옳다고 말하면, 그것을 사용하지 않는 선언을 읽는 방법에 대한 설명을하고 사실 모순입니다. –

+0

당신이 재귀 할 때 (나는 둥지라는 용어를 사용 하겠지만 같은 것을 사용한다.), 선언의 중첩 된 부분은'base-part'를 포함하지 않는다는 것을 언급 할 수있다. (함수 매개 변수를 제외하고는 다른 선언에 중첩 된 완전한 선언을 가짐). –

+0

@JamesKanze 당신이 언급하고 싶은 것은 즉시 명백해야합니다. 'object is ... '아래에'base-type'이 없다는 것을 관찰하십시오. –

16

기본적으로 말해서, 규칙은 간단하게 예를 들어, 일, 그렇지 않으면 일을하는 경우, 는 아무 소용이 없다 (나선형 무엇을 의미하는지 재정 의하여 고려하지 않습니다.

int* a[10][15]; 

나선형 규칙은 int 배열의 배열 [15]에 대한 포인터의 배열 [10]을 제공합니다.이 사이트의 경우 도 작동하지 않으며 실제로는 signal의 경우 , 나선형을 시작해야 할 곳이 심지어 이 아닙니다.

일반적으로 규칙이 작동하지 않는 예제보다 더 쉽게 규칙을 찾을 수 있습니다.

나는 C++ 선언을 구문 분석하는 것이 간단하지만 이라는 복잡한 말을하고 있지만 복잡한 선언을 시도한 사람이 나를 믿을 수는 없다는 말을 듣고 싶다. 반면에, 그것은 때로는 만들어진 만큼 어렵지 않습니다. 비밀은 선언을 표현식과 똑같이 생각하지만, 연산자가 더 적어서 이 적고 매우 간단한 선행 규칙이 있습니다. 오른쪽에있는 모든 연산자 이 왼쪽의 모든 연산자보다 우선합니다. 에는 괄호가 없으므로 의 모든 것을 먼저 처리 한 다음 왼쪽의 모든 것을 처리하고 괄호를 다른 표현식에서와 똑같이 처리해야합니다. 실제 어려움 하지 구 자체이지만 결과한다는 것은 아주 복잡하고 직관적 선언 기능 함수 반환 값과 포인터가 관여 특히 이다 : 제 오른쪽 후 좌측 규칙 수단 특정 수준의 연산자는 종종 멀리 떨어져 있는지, 예 :

int (*f(/* lots of parameters */))[10]; 

확장의 마지막 용어는 여기 int[10]하지만에서 (전체 기능 사양 후 [10]을 입니다 퍼팅적어도 내게) 매우 부자연스럽고, 나는 그만하고 매번 을 해결해야합니다. 논리적으로 인접한 부분이 확산되어 나선형 규칙으로 이어지는 경향이있는 것입니다. 문제는 은 물론 괄호가없는 경우 [i][j]을 볼 때마다 은 항상 —으로 퍼지지 않습니다. 규칙은 권리를 이동하고, 다음으로 이동 다시 오른쪽, 오히려

) 나선 것보다 우리가 지금 표현의 측면에서 선언 생각하고 가입일 :. 당신이 표현이 너무 읽을 복잡하게 될 때 무엇을해야합니까? 더 쉽게 읽을 수 있도록 중간 변수를 순서로 소개합니다. 선언의 경우 "중간 변수"는 typedef입니다. 특히 은 반환 형식의 일부가 함수 인수 (및 다른 많은 시간이 지나면)로 끝나면 은 typedef을 사용해야보다 간단하게 선언 할 수 있습니다. ( 은 "내가하는 것처럼하지 말라"는 규칙이다. 나는 두렵다. 때때로 매우 복잡한 선언문을 사용하기도한다.)

+0

"괄호가 없으면 모든 것을 왼쪽에서 먼저 처리 한 다음 모든 것을 오른쪽에서 처리합니다." 다른 길은 아닌가? 당신이 말했듯이, 오른쪽에있는 모든 운영자는 왼쪽에있는 모든 운영자보다 우선합니다. 따라서 첫 번째는 오른쪽으로, 그 다음은 왼쪽으로 이동해야합니다. – AnT

+0

@AnT 예. 나는 분명히 그 진술을 잘못 입력했다. 고마워. 고마워. (그리고 지금까지 아무도 그 오류에 주목하지 않았다는 것은 놀랍습니다.) –

관련 문제