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;
}
첫 번째 예에서 오류는 무엇을 의미합니까?
답변
printf("%d",a+++++b);
(a++)++ + b
Maximal Munch Rule에 따라 해석됩니다 ! .
++
(접미사)는로 평가되지 않지만 lvalue
피연산자가 lvalue
.
! 6.4 / 4는 다음 전처리 토큰이 전처리 토큰을 구성 할 수있는 가장 긴 문자 시퀀스라고 말합니다. ”
답변
컴파일러는 단계적으로 작성됩니다. 첫 번째 단계는 렉서 (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
괜찮습니다. 다른 예도 마찬가지입니다.
답변
어휘 분석기는 일반적으로 “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)를 사용하여 컴파일하고 실행합니다 (아무것도하지 않음).
답변
이 정확한 예는 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!
^^
*=
할당 연산자 로 해석됩니다 .이 경우 해결책은 선언에서 매개 변수의 이름을 지정하는 것입니다.
답변
당신의 컴파일러는 필사적으로 파싱을 시도하고 a+++++b
그것을 (a++)++ +b
. 이제 post-increment ( a++
) 의 결과 는 lvalue 가 아닙니다. 즉, 다시 post-increment 할 수 없습니다.
프로덕션 품질 프로그램에서 이러한 코드를 작성하지 마십시오. 당신의 코드를 해석해야하는 가난한 사람이 당신을 쫓는 것에 대해 생각해보십시오.
답변
(a++)++ +b
a ++는 이전 값인 rvalue를 반환합니다. 이것을 증가시킬 수 없습니다.
답변
정의되지 않은 동작을 유발하기 때문입니다.
어떤거야?
c = (a++)++ + b
c = (a) + ++(++b)
c = (a++) + (++b)
예, 당신도 컴파일러도 그것을 모릅니다.
편집하다:
진짜 이유는 다른 사람들이 말한 것입니다.
로 해석됩니다 (a++)++ + b
.
그러나 post increment에는 lvalue (이름이있는 변수)가 필요하지만 (a ++)는 증가 할 수없는 rvalue를 반환하므로 오류 메시지가 표시됩니다.
이것을 지적하는 다른 사람들에게 Thx.