c = ++ (a + b)에서 컴파일 오류가 발생하는 이유는 무엇입니까?


111

조사한 후 증분 연산자에는 피연산자가 수정 가능한 데이터 개체가 필요하다는 것을 읽었습니다. https://en.wikipedia.org/wiki/Increment_and_decrement_operators .

이것으로부터 나는 (a+b)임시 정수이므로 수정할 수 없기 때문에 컴파일 오류가 발생한다고 생각합니다 .

이 이해가 맞습니까? 문제를 조사하는 것은 이번이 처음 이니 찾아보아야 할 것이 있다면 조언 해주십시오.


35
연구 측면에서 나쁘지 않습니다. 당신은 올바른 길을 가고 있습니다.
StoryTeller-Unslander Monica

35
표현이 무엇을 할 것으로 기대합니까?
qrdl

4
C11 표준 6.5.3.1에있어서, 프리픽스 증가 또는 감소 연산자의 연산이 실제 또는 포인터 타입, 정규화 원자 또는 비정규를 가진다 및 수정 좌변한다
크리스찬 기븐스

10
1을 a와 b 사이에 어떻게 배포 하시겠습니까? "배열 인덱스는 0 또는 1에서 시작해야합니까? 0.5의 타협은 적절한 고려없이 거부되었습니다." — Stan Kelly-Bootle
Andrew Morton

5
질문에 대한 후속 질문은 c = a + b + 1의도를 더 명확하게 만들고 입력하기가 더 짧을 때 왜 이렇게하고 싶습니까 ? 증가 / 감소 연산자는 두 가지 작업을 수행합니다. 1. 해당 연산자와 인수가 표현식을 형성합니다 (예 : for 루프에서 사용할 수 있음). 2. 인수를 수정합니다. 귀하의 예에서는 수정 된 인수를 버리기 때문에 속성 1을 사용하고 속성 2를 사용하지 않습니다. 속성 2가 필요하지 않고 식만 원하는 경우 식을 작성할 수 있습니다 (예 : x ++ 대신 x + 1).
Trevor

답변:


117

그것은 단지 규칙 일 뿐이며 (1) C 컴파일러를 더 쉽게 작성하고 (2) 아무도 C 표준위원회가이를 완화하도록 설득하지 않았습니다.

비공식적으로 말하면 과 같은 할당 표현식의 왼쪽에 can이 나타날 때만 쓸 ++foofoo있습니다 foo = bar. 당신이 쓸 수 없기 때문에 당신도 쓸 a + b = bar수 없습니다 ++(a + b).

작동 할 수 a + b있는 임시를 산출 할 수 없는 실제 이유 는 없으며 ++그 결과는 표현식의 값입니다 ++(a + b).


4
나는 포인트 (1)이 머리에 못을 박았다고 생각합니다. C ++에서 일시적인 구체화에 대한 규칙을 보는 것만으로도 배가 될 수 있습니다 (하지만 강력하지만 그렇게 말해야합니다).
StoryTeller-Unslander Monica

4
@StoryTeller : 실제로 우리가 사랑하는 언어 인 C ++와 달리 C는 여전히 비교적 사소하게 어셈블리로 컴파일됩니다.
Bathsheba

29
IMHO의 진정한 이유는 다음과 같습니다 ++. 때때로 무언가를 수정하는 부작용이 있고 때로는 그렇지 않은 경우 끔찍한 혼란이 될 것 입니다.
aschepler

5
@dng : 실제로 그렇습니다. 그렇기 때문에 lvalue와 rvalue라는 용어가 도입되었습니다.하지만 요즘에는 (특히 C ++에서) 상황이 더 복잡합니다. 예를 들어 상수는 lvalue가 될 수 없습니다. 5 = a와 같은 것은 의미가 없습니다.
Bathsheba

6
@Bathsheba 5 ++도 컴파일 오류를 일으키는 이유를 설명합니다
dng

40

섹션 6.5.3.1의 C11 표준 상태

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

"수정 가능한 lvalue"는 섹션 6.3.2.1 하위 섹션 1에 설명되어 있습니다.

lvalue는 객체를 잠재적으로 지정하는 표현식 (void 이외의 객체 유형)입니다. lvalue가 평가 될 때 객체를 지정하지 않으면 동작이 정의되지 않습니다. 객체가 특정 유형을 갖는다 고 할 때 유형은 객체를 지정하는 데 사용되는 lvalue에 의해 지정됩니다. 수정 가능한 lvalue는 배열 유형이없고 불완전한 유형이없고 const 규정 된 유형이 없으며 구조 또는 공용체 인 경우 멤버가없는 lvalue입니다 (재귀 적으로 모든 멤버 포함). 또는 포함 된 모든 집계 또는 공용체의 요소)를 const 한정 형식으로 지정합니다.

그래서 (a+b)수정 가능한 좌변 아니므로 접두사 증가 연산자에 적합하지 않습니다.


1
이러한 정의에 대한 결론이 누락되었습니다 ... (a + b)가 잠재적으로 객체를 지정하지 않는다고 말하고 싶지만이 단락에서는이를 허용하지 않습니다.
hkBst

21

당신이 올바른지. 은 ++원래 변수에 새로운 값을 할당하려고합니다. 따라서 ++a의 값을 가져 와서 a추가 1한 다음에 다시 할당합니다 a. 말했듯이, (a + b)는 임시 값이고 할당 된 메모리 주소가있는 변수가 아니기 때문에 할당을 수행 할 수 없습니다.


12

대부분 자신의 질문에 대답 한 것 같습니다. C.Gibbons가 언급했듯이 구문을 약간 변경하고 "임시 변수"를 "rvalue"로 바꿀 수 있습니다.

변수, 인수, 임시 변수 등의 용어는 C의 메모리 모델에 대해 배울수록 더 명확해질 것입니다 (멋진 개요처럼 보입니다 : https://www.geeksforgeeks.org/memory-layout-of-c-program/). ).

"rvalue"라는 용어는 처음 시작할 때 불투명 해 보일 수 있으므로 다음 내용이 이에 대한 직관을 개발하는 데 도움이되기를 바랍니다.

Lvalue / rvalue는 등호 (할당 연산자)의 다른 측면에 대해 설명합니다. lvalue = 왼쪽 ( "일"이 아닌 소문자 L) rvalue = 오른쪽

C가 메모리 (및 레지스터)를 사용하는 방법에 대해 조금 배우면 구별이 중요한 이유를 확인하는 데 도움이됩니다. 에서 폭 넓은 브러시 스트로크 , 컴파일러는 표현합니다 (를 rvalue)의 결과를 계산하는 기계 언어 명령어의 목록을 만든 다음 박았 그 결과 어딘가합니다 (좌변)을. 다음 코드 조각을 처리하는 컴파일러를 상상해보십시오.

x = y * 3

어셈블리 의사 코드에서는 다음과 같은 장난감 예제와 같이 보일 수 있습니다 .

load register A with the value at memory address y
load register B with a value of 3
multiply register A and B, saving the result in A
write register A to memory address x

++ 연산자 (및 그에 상응하는-)는 수정할 "어딘가", 본질적으로 lvalue로 작동 할 수있는 모든 것이 필요합니다.

C 메모리 모델을 이해하면 인수가 함수에 전달되는 방법과 (결국) malloc () 함수와 같은 동적 메모리 할당을 사용하는 방법에 대해 더 나은 아이디어를 얻을 수 있기 때문에 도움이 될 것입니다. 비슷한 이유로 컴파일러가 수행하는 작업에 대한 더 나은 아이디어를 얻기 위해 어떤 시점에서 간단한 어셈블리 프로그래밍을 공부할 수 있습니다. 또한 gcc 를 사용하는 경우 -S 옵션 "적절한 컴파일 단계 후에 중지하십시오. 어셈블하지 마십시오." 흥미로울 수 있습니다 ( 작은 코드 조각 에서 시도하는 것이 좋습니다 ).

제쳐두고 : ++ 명령어 는 1969 년부터 사용 되어 왔습니다 (C의 전임자 B에서 시작되었지만).

(Ken Thompson의) 관찰은 ++ x의 번역이 x = x + 1의 번역보다 작다는 것이 었습니다. "

위키피디아 참조를 따라 가면 편리하게 여기에 링크 된 C 언어의 역사에 대한 Dennis Ritchie ( "K & R C"의 "R")의 흥미로운 글을 볼 수 있습니다. http://www.bell-labs.com/ usr / dmr / www / chist.html 여기서 "++"를 검색 할 수 있습니다.


6

그 이유는 표준에서 피연산자가 lvalue 여야하기 때문입니다. 표현식 (a+b)이 lvalue가 아니므로 증분 연산자를 적용 할 수 없습니다.

이제, 하나는 말할 수있다 "정말로 이유지만, 실제로는 더 * 진짜 *의보다 다른 이유가 없다는 것을 확인" 하지만 불행하게도 운전자가 사실로 작동하는 방법의 특정 문구 않는 경우가 해당이 필요가.

표현식 ++ E는 (E + = 1)과 같습니다.

물론, 당신은 쓸 수없는 E += 1경우 E좌변이 아니다. "E를 하나씩 증가시킨다" 고 말할 수 있었기 때문에 부끄러운 일입니다. 이 경우, 비 lvalue에 연산자를 적용하는 것은 (원칙적으로) 컴파일러를 약간 더 복잡하게 만드는 대신 완벽하게 가능합니다.

이제 정의는 사소하게 바꿀 수 있지만 (원래 C도 아니지만 B의 가보라고 생각합니다) 그렇게하면 근본적으로 언어가 이전 버전과 더 이상 호환되지 않는 언어로 변경됩니다. 가능한 이점은 다소 적지 만 가능한 의미는 엄청 나기 때문에 결코 일어나지 않았으며 아마도 결코 일어나지 않을 것입니다.

C 외에 C ++를 고려한다면 (질문은 C로 태그되었지만 연산자 오버로드에 대한 논의가있었습니다) 이야기는 훨씬 더 복잡해집니다. C에서는 이것이 사실 일 수 있다고 상상하기 어렵지만 C ++에서는 그 결과가 (a+b)전혀 증가 할 수없는 것이거나 증가하는 것이 매우 상당한 부작용을 가질 수 있습니다 (단지 1을 추가하는 것이 아니라). 컴파일러는 이에 대처할 수 있어야하며 문제 발생시 문제를 진단 할 수 있어야합니다. lvalue에서는 여전히 확인하기가 쉽지 않습니다. 괄호 안의 어떤 종류의 우연한 표현도 당신이 가난한 것을 던지는 것은 아닙니다.
이것이 불가능한 진짜 이유 가 아닙니다. 그러나이를 구현 한 사람들이 소수의 사람들에게 거의 이익을 약속하지 않는 그러한 기능을 추가하는 것이 정확히 황홀하지 않은 이유를 설명 할 수 있습니다.



3

++는 원래 변수에 값을 제공하려고 시도하며 (a + b)는 임시 값이므로 연산을 수행 할 수 없습니다. 그리고 그것들은 기본적으로 프로그래밍을 쉽게하기위한 C 프로그래밍 규칙의 규칙입니다. 그게 다야.


2

++ (a + b) 표현식이 수행되면 예를 들면 다음과 같습니다.

int a, b;
a = 10;
b = 20;
/* NOTE :
 //step 1: expression need to solve first to perform ++ operation over operand
   ++ ( exp );
// in your case 
   ++ ( 10 + 20 );
// step 2: result of that inc by one 
   ++ ( 30 );
// here, you're applying ++ operator over constant value and it's invalid use of ++ operator 
*/
++(a+b);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.