a +++++ b가 작동하지 않는 이유는 무엇입니까?


89
int main ()
{
   int a = 5,b = 2;
   printf("%d",a+++++b);
   return 0;
}

이 코드는 다음 오류를 제공합니다.

오류 : 증가 피연산자로 lvalue 필요

하지만 전반에 걸쳐 공간을 세우면 a++ +하고 ++b다음 잘 작동합니다.

int main ()
{
   int a = 5,b = 2;
   printf("%d",a++ + ++b);
   return 0;
}

첫 번째 예에서 오류는 무엇을 의미합니까?


3
당신이 묻는 정확한 표현이 C99 및 C11 표준의 예로 사용된다는 사실을 아무도 발견하지 못한 것은 놀라운 일입니다. 또한 좋은 설명을 제공합니다. 나는 그것을 내 대답에 포함시켰다.
Shafik Yaghmour 2014

@ShafikYaghmour — C11 §6.4 어휘 요소 ¶6 의 '예제 2'입니다 . 그것은 말한다 "프로그램 단편 x+++++y으로 구문 분석 x ++ ++ + y구문 분석에도 불구하고, 증가 연산자에 대한 제약 조건을 위반하는 x ++ + ++ y올바른 표현을 얻을 수 있습니다."
Jonathan Leffler

답변:


98

printf("%d",a+++++b);(a++)++ + bMaximal Munch Rule에 따라 해석됩니다 ! .

++(접미사)는로 평가되지 않지만 lvalue피연산자가 lvalue.

! 6.4 / 4는 다음 전처리 토큰이 전처리 토큰을 구성 할 수있는 가장 긴 문자 시퀀스라고 말합니다. "


181

컴파일러는 단계적으로 작성됩니다. 첫 번째 단계는 렉서 (lexer)라고하며 문자를 기호 구조로 바꿉니다. 그래서 "++"는enum SYMBOL_PLUSPLUS . 나중에 파서 단계는 이것을 추상 구문 트리로 바꾸지 만 기호를 변경할 수는 없습니다. 공백을 삽입하여 어휘 분석기에 영향을 줄 수 있습니다 (따옴표로 묶이지 않은 경우 기호를 끝냄).

일반 렉서는 탐욕 스럽기 때문에 (일부 예외가 있음) 코드는 다음과 같이 해석됩니다.

a++ ++ +b

파서에 대한 입력은 심볼 스트림이므로 코드는 다음과 같습니다.

[ SYMBOL_NAME(name = "a"), 
  SYMBOL_PLUS_PLUS, 
  SYMBOL_PLUS_PLUS, 
  SYMBOL_PLUS, 
  SYMBOL_NAME(name = "b") 
]

구문 분석기가 구문 상 잘못되었다고 생각하는 것. (댓글을 기반으로 편집 : ++를 r- 값에 적용 할 수 없기 때문에 의미 상 잘못됨, a ++ 결과)

a+++b 

이다

a++ +b

괜찮습니다. 다른 예도 마찬가지입니다.


27
+1 좋은 설명. 그래도 nitpick해야합니다 : 구문 적으로 정확하고 의미 오류가 있습니다 (에서 발생하는 lvalue 증가 시도 a++).

7
a++결과는 rvalue입니다.
Femaref 2011-04-15

9
어휘 분석기의 맥락에서 '탐욕스러운'알고리즘은 일반적으로 Maximal Munch ( en.wikipedia.org/wiki/Maximal_munch ) 라고 합니다.
JoeG 2011

14
좋은. 욕심 많은 어휘 덕분에 많은 언어에 비슷한 기괴한 코너 케이스가 있습니다. 표현식을 길게 만들면 더 좋게 만드는 정말 이상한 것이 있습니다. VBScript x = 10&987&&654&&321에서는 불법이지만 기이하게도 충분히 x = 10&987&&654&&&321합법적입니다.
Eric Lippert 2011

1
탐욕과는 아무 상관이없고, 질서와 우선 순위와는 상관이 없습니다. ++가 +보다 높으므로 두 개의 ++가 먼저 수행됩니다. +++++ b도 ++ ++ + b가 아니라 + ++ ++ b가됩니다. 링크에 대한 @MByD에 대한 크레딧.

30

어휘 분석기는 일반적으로 "maximum munch"알고리즘을 사용하여 토큰을 만듭니다. 즉, 문자를 읽을 때 이미 가지고있는 것과 동일한 토큰의 일부가 될 수없는 무언가를 만날 때까지 계속 문자를 읽습니다 (예 : 숫자를 읽었으므로 숫자가있는 경우, an A, 그것은 숫자의 일부가 될 수 없다는 것을 알고 있습니다. 그래서 그것은 멈추고 A다음 토큰의 시작으로 사용하기 위해 입력 버퍼에 남겨 둡니다 ). 그런 다음 해당 토큰을 파서에 반환합니다.

이 경우, 그 수단 +++++으로 lexed됩니다 a ++ ++ + b. 첫 번째 사후 증가는 rvalue를 산출하므로 두 번째는 적용 할 수 없으며 컴파일러는 오류를 제공합니다.

FWIW 만 있으면 C ++에서 오버로드 operator++하여 lvalue를 생성 할 수 있습니다 . 예를 들면 :

struct bad_code { 
    bad_code &operator++(int) { 
        return *this;
    }
    int operator+(bad_code const &other) { 
        return 1;
    }
};

int main() { 
    bad_code a, b;

    int c = a+++++b;
    return 0;
}

내가 가지고있는 C ++ 컴파일러 (VC ++, g ++, Comeau)를 사용하여 컴파일하고 실행합니다 (아무것도하지 않음).


1
"예를 들어, 무엇을 가지고하는 숫자이며,이는 A가 발생하면, 그것은 그 숫자의 일부가 될 수 없습니다 알 수 있도록이 자리를 읽고 않다면" 16FA입니다 완벽하게 잘 진수 번호 가 A 포함
orlp을

1
@nightcracker : 예,하지만 0x시작 부분 16FA가 없으면 단일 16 진수가 아닌 뒤에 오는 것으로 처리됩니다 .
Jerry Coffin 2011

@Jerry Coffin : 당신은 0x숫자의 일부 가 아니라고 말하지 않았습니다.
orlp 2011-04-15

@nightcracker : 아니요, 저는하지 않았습니다 x.
Jerry Coffin

14

이 정확한 예는 C99 표준 초안 ( C11의 세부 사항과 동일 ) 섹션 6.4 어휘 요소 단락 4 에서 다룹니다 .

입력 스트림이 주어진 문자까지 사전 처리 토큰으로 구문 분석 된 경우 다음 사전 처리 토큰은 사전 처리 토큰을 구성 할 수있는 가장 긴 문자 시퀀스입니다. [...]

모호함을 피하기 위해 어휘 분석에 사용되는 최대 뭉크 규칙 으로도 알려져 있으며 유효한 토큰을 형성하기 위해 가능한 한 많은 요소를 취하여 작동합니다.

단락에는 또한 두 번째 예가 질문에 정확히 일치하며 다음과 같은 두 가지 예가 있습니다.

예 2 프로그램 조각 x +++++ y는 x ++ ++ + y로 구문 분석되며, 이는 x ++ + ++ y 구문 분석이 올바른 식을 생성 할 수 있지만 증가 연산자에 대한 제약 조건을 위반합니다.

이는 우리에게 다음을 알려줍니다.

a+++++b

다음과 같이 구문 분석됩니다.

a ++ ++ + b

첫 번째 포스트 증분의 결과는 rvalue이고 포스트 증분에는 lvalue가 필요하기 때문에 포스트 증분에 대한 제약 조건을 위반합니다. 이 절에서 다루고있다 6.5.2.4 후위 증가 및 감소 연산자 (라고 강조 광산 ) :

접미사 증가 또는 감소 연산자의 피연산자는 규정 된 또는 규정되지 않은 실수 또는 포인터 유형을 가져야 하며 수정 가능한 lvalue 여야합니다.

접미사 ++ 연산자의 결과는 피연산자의 값입니다.

이 책은 C ++ 둘점은 또한이 경우 커버 Gotcha #17 최대한 뭉크 문제 는 동일한 문제가 C ++ 뿐만 아니라 그것은 또한 몇 가지 예를 제공합니다. 다음 문자 집합을 처리 할 때 설명합니다.

->*

어휘 분석기는 다음 세 가지 중 하나를 수행 할 수 있습니다.

  • 세 개의 토큰으로 취급 : -, >*
  • 이 토큰로 취급 : ->*
  • 하나의 토큰으로 취급하십시오. ->*

최대 뭉크의 규칙은 이러한 모호성을 피할 수 있습니다. 저자는 다음과 같이 지적합니다 ( C ++ 컨텍스트에서 ) :

원인보다 더 많은 문제를 해결하지만, 두 가지 일반적인 상황에서는 성가신 일입니다.

첫 번째 예는 템플릿 인수도 템플릿 ( C ++ 11에서 해결됨) 인 템플릿 입니다. 예를 들면 다음과 같습니다.

list<vector<string>> lovos; // error!
                  ^^

닫는 꺾쇠 괄호를 시프트 연산자 로 해석 하므로 명확성을 위해 공백이 필요합니다.

list< vector<string> > lovos;
                    ^

두 번째 경우에는 포인터에 대한 기본 인수가 포함됩니다. 예를 들면 다음과 같습니다.

void process( const char *= 0 ); // error!
                         ^^

*=할당 연산자 로 해석됩니다 .이 경우 해결책은 선언에서 매개 변수의 이름을 지정하는 것입니다.


C ++ 11의 어떤 부분이 최대 뭉침 규칙을 말하는지 아십니까? 2.2.3, 2.5.3은 흥미롭지 만 C만큼 명시 적이지는 않습니다. >>규칙은 다음에서 요청됩니다. stackoverflow.com/questions/15785496/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
@CiroSantilli巴拿馬文件六四事件法轮功참조 여기이 대답
Shafik Yaghmour

감사합니다. 제가 지적한 섹션 중 하나입니다. 내 캡 ;-) 기운이 떨어지기 때 나는 내일을 upvote에 있습니다
치로 틸리가郝海东冠状病六四事件法轮功

12

당신의 컴파일러는 필사적으로 파싱을 시도하고 a+++++b그것을 (a++)++ +b. 이제 post-increment ( a++) 의 결과 는 lvalue 가 아닙니다. 즉, 다시 post-increment 할 수 없습니다.

프로덕션 품질 프로그램에서 이러한 코드를 작성하지 마십시오. 당신의 코드를 해석해야하는 가난한 사람이 당신을 쫓는 것에 대해 생각해보십시오.


10
(a++)++ +b

a ++는 이전 값인 rvalue를 반환합니다. 이것을 증가시킬 수 없습니다.


7

정의되지 않은 동작을 유발하기 때문입니다.

어떤거야?

c = (a++)++ + b
c = (a) + ++(++b)
c = (a++) + (++b)

예, 당신도 컴파일러도 그것을 모릅니다.

편집하다:

진짜 이유는 다른 사람들이 말한 것입니다.

로 해석됩니다 (a++)++ + b.

그러나 post increment에는 lvalue (이름이있는 변수)가 필요하지만 (a ++)는 증가 할 수없는 rvalue를 반환하므로 오류 메시지가 표시됩니다.

이것을 지적하는 다른 사람들에게 Thx.


5
a +++ b-(a ++) + b와 a + (++ b)에 대해 동일한 결과를 얻을 수 있습니다.
Michael Chinen 2011

4
실제로, 후위는 ++, 그래서 접두사 ++보다 더 높은 우선 순위를 가지고 있습니다 a+++b항상a++ + b
MByD

4
나는 이것이 정답이라고 생각하지 않지만 틀릴 수 있습니다. 나는 어휘 분석기가 그것을 a++ ++ +b파싱 ​​할 수없는 것으로 정의한다고 생각한다 .
Lou Franco

2
이 답변에 동의하지 않습니다. '정의되지 않은 행동'은 토큰 화 모호성과는 상당히 다릅니다. 그리고 문제도 없다고 생각합니다.
Jim Blackler 2011-04-15

2
지금 ... 내보기 "그렇지 않으면 +++++ (B)가 ((A ++) ++) + B로 평가 것"입니다 a+++++b 않습니다 로 평가 (a++)++)+b. 확실히 GCC에서는 이러한 대괄호를 삽입하고 다시 빌드하면 오류 메시지가 변경되지 않습니다.
Jim Blackler 2011-04-15

5

나는 컴파일러가 그것을 다음과 같이 본다고 생각한다.

c = ((a ++) ++) + b

++피연산자로 수정할 수있는 값을 가져야합니다. a는 수정할 수있는 값입니다. a++그러나 'rvalue'는 수정할 수 없습니다.

그건 그렇고 내가 GCC C에서 보는 오류는 동일하지만 다른 단어로 표시 lvalue required as increment operand됩니다.


0

이 선행 순서를 따르십시오

1. ++ (사전 증분)

2. +-(더하기 또는 빼기)

3. "x"+ "y"두 시퀀스 추가

int a = 5,b = 2; printf("%d",a++ + ++b); //a is 5 since it is post increment b is 3 pre increment return 0; //it is 5+3=8

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.