i = (i, ++ i, 1) + 1은 무엇입니까? 하다?


174

정의되지 않은 동작 및 시퀀스 포인트에 대한 이 답변을 읽은 후 작은 프로그램을 작성했습니다.

#include <stdio.h>

int main(void) {
  int i = 5;
  i = (i, ++i, 1) + 1;
  printf("%d\n", i);
  return 0;
}

출력은 2입니다. 오 하나님, 나는 감소가 오는 것을 보지 못했습니다! 여기서 무슨 일이 일어나고 있습니까?

또한 위의 코드를 컴파일하는 동안 경고 메시지가 나타납니다.

px.c : 5 : 8 : 경고 : 쉼표 식의 왼쪽 피연산자는 효과가 없습니다.

  [-Wunused-value]   i = (i, ++i, 1) + 1;
                        ^

왜? 그러나 아마도 첫 번째 질문에 대한 답변으로 자동 응답 될 것입니다.


289
이상한 일을하지 마십시오. 친구가 없습니다 :(
Maroun

9
경고 메시지는 첫 번째 질문에 대한 답변입니다.
Yu Hao

2
@gsamaras : 아뇨. 결과 은 수정이 아닌 삭제됩니다. 실제 답변 : 쉼표 연산자는 시퀀스 포인트를 만듭니다.
Karoly Horvath 2016 년

3
@gsamaras 10 점 이상의 질문으로 긍정적 인 점수를 받았을 때 신경 쓰지 않아도됩니다.
LyingOnTheSky 2016 년

9
참고 : 최적화 컴파일러는 다음과 같이 간단합니다.printf("2\n");
chux-Reinstate Monica

답변:


256

식에서 (i, ++i, 1)쉼표는 쉼표 연산자입니다.

쉼표 연산자 (token으로 ,표시됨)는 첫 번째 피연산자를 평가하고 결과를 버린 다음 두 번째 피연산자를 평가하여이 값 (및 유형)을 리턴하는 2 진 연산자입니다.

첫 번째 피연산자가 삭제되므로 일반적으로 첫 번째 피연산자가 바람직한 부작용을 갖는 경우에만 유용합니다 . 첫 번째 피연산자에 대한 부작용이 발생하지 않으면 컴파일러는 아무런 영향없이 표현식에 대한 경고를 생성 할 수 있습니다.

따라서 위의 식에서 가장 왼쪽 i이 평가되고 해당 값이 삭제됩니다. 그런 다음 ++i평가 i되고 1 씩 증가 하고 식의 값은 ++i무시 되지만 부작용 i은 영구적 입니다. 그런 다음 1평가되고 표현식의 값은입니다 1.

그것은

i;          // Evaluate i and discard its value. This has no effect.
++i;        // Evaluate i and increment it by 1 and discard the value of expression ++i
i = 1 + 1;  

참고 상기 식 완벽 유효하고 정의되지 않은 동작 호출하지 않는 있기 때문에 순서 포인트 콤마 연산자의 좌우 피연산자의 평가 사이가.


1
최종 표현식은 유효하지만 두 번째 표현식 ++ i는 정의되지 않은 동작입니까? 그것은 초기화되고 초기화되지 않은 변수의 값은 미리 증분됩니다. 아니면 뭔가 빠졌습니까?
Koushik Shetty 2016 년

2
@Koushik; i로 초기화됩니다 5. 선언문을보십시오 int i = 5;.
haccks

1
오 내 나쁜. 죄송합니다.
Koushik Shetty 2016 년

여기에 실수가 있습니다 : ++ i는 i를 증가시킨 다음 평가하고 i ++는 i를 평가 한 다음 증가시킵니다.
Quentin Hayot

1
@QuentinHayot; 뭐? 모든 부작용은 발현 평가 후 발생합니다. 의 ++i경우이 표현식이 평가되고 i증가되며이 증가 된 값은 표현식의 값이됩니다. 의 i++경우이 표현식이 평가되고, 이전 값이 표현식 i의 값이되고, 표현식 i의 이전 및 다음 시퀀스 지점 사이에서 언제든지 증가합니다.
haccks

62

C11, 챕터 6.5.17, 쉼표 연산자 인용

쉼표 연산자의 왼쪽 피연산자는 void 표현식으로 평가됩니다. 평가와 올바른 피연산자의 평가 사이에는 순서 점이 있습니다. 그런 다음 올바른 피연산자가 평가됩니다. 결과 유형과 값이 있습니다.

따라서 귀하의 경우에는

(i, ++i, 1)

로 평가된다

  1. i, void 표현식으로 평가되고 값이 삭제됩니다.
  2. ++i, void 표현식으로 평가되고 값이 삭제됩니다.
  3. 마지막으로 1값이 반환되었습니다.

그래서 마지막 문장은

i = 1 + 1;

i도착합니다 2. 나는 이것이 당신의 두 질문에 모두 대답한다고 생각합니다.

  • i값 2는 어떻게 얻습니까?
  • 경고 메시지가 나타나는 이유는 무엇입니까?

참고 : FWIW, 왼쪽 피연산자 평가 후 시퀀스 포인트(i, ++i, 1)있으므로 일반적으로 실수로 생각할 수 있으므로 UB를 호출하지 않습니다 .


+1 Sourav, 이것은 왜 초기화가 i분명히 효과가 없는지 설명하기 때문입니다! 그러나 쉼표 연산자를 모르는 사람에게는 그렇게 분명하지 않다고 생각합니다 (질문을 묻는 것 외에 도움을 찾는 방법을 알지 못했습니다). 동정은 너무 많은 downvotes를 얻었다! 다른 답변을 확인한 후 어느 것을 수락할지 결정하겠습니다. 감사! 좋은 대답 btw.
gsamaras

왜 내가 왜 하크 응답을 받아들 였는지 설명해야한다고 느낀다. 두 질문에 모두 대답하기 때문에 당신의 의견을 받아 들일 준비가되었습니다. 그러나 내 질문에 대한 의견을 확인하면 일부 사람들이 왜 이것이 UB를 호출하지 않는지를 한눈에 볼 수 없다는 것을 알 수 있습니다. haccks 답변은 관련 정보를 제공합니다. 물론, 내 질문에 링크 된 UB에 대한 답변이 있지만 일부 사람들은 그것을 놓칠 수 있습니다. 알려주지 않으면 내 desicion에 동의하기를 바랍니다. :)
gsamaras 2016 년

30
i = (i, ++i, 1) + 1;

단계별로 분석해 봅시다.

(i,   // is evaluated but ignored, there are other expressions after comma
++i,  // i is updated but the resulting value is ignored too
1)    // this value is finally used
+ 1   // 1 is added to the previous value 1

그래서 우리는 2를 얻습니다. 그리고 마지막 과제는 :

i = 2;

지금 덮어 쓰기 전에 i에 있던 것이 무엇이든간에 .


쉼표 연산자로 인해 이러한 상황이 발생한다는 것이 좋을 것입니다. 단계별 분석으로 +1! 좋은 대답 btw.
gsamaras

설명이 충분하지 않아서 죄송합니다. 여기에 메모 만 있습니다 ( ...하지만 무시하고 있습니다 ... ). 나는 왜 그것이 ++i결과에 기여하지 않는지 주로 설명하고 싶었다 .
dlask

이제 내 for 루프는 항상 같습니다int i = 0; for( ;(++i, i<max); )
CoffeDeveloper

19

의 결과

(i, ++i, 1)

이다

1

에 대한

(i,++i,1) 

평가는 ,운영자가 평가 된 값을 버리고 가장 정확한 값을 유지하도록 수행됩니다.1

그래서

i = 1 + 1 = 2

1
그래, 나도 그렇게 생각했지만 왜 그런지 모르겠어!
gsamaras

@gsamaras 쉼표 연산자는 이전 용어를 평가하지만 폐기하기 때문에 (즉, 할당 등에 사용하지 않음)
Marco A.

14

쉼표 연산자 에 대한 위키 페이지에서 유용한 정보를 찾을 수 있습니다.

기본적으로

... 첫 번째 피연산자를 평가하고 결과를 버린 다음 두 번째 피연산자를 평가하고이 값 (및 유형)을 반환합니다.

이것은

(i, i++, 1)

차례로 평가합니다 i, 그 결과를 폐기, 평가하고 i++, 그 결과를 폐기하고 평가하고 반환 1.


O_O 지옥, 구문이 C ++에서 유효합니까, 나는 그 구문이 필요한 곳이 거의 없다는 것을 기억합니다 (기본적으로 내가 쓴 것 : (void)exp; a= exp2;방금 필요했습니다 a = exp, exp2;)
CoffeDeveloper

13

쉼표 연산자가 무엇을하고 있는지 알아야합니다.

당신의 표현 :

(i, ++i, 1)

첫 번째 표현식 ( i)이 평가되고 두 ​​번째 표현식 ( ++i)이 평가되며 세 번째 표현식 ( 1)이 전체 표현식에 대해 리턴됩니다.

결과는 다음과 같습니다 i = 1 + 1.

보너스 질문의 경우 첫 번째 표현 i은 전혀 효과가 없으므로 컴파일러가 불평합니다.


5

쉼표에는 '역'우선 순위가 있습니다. IBM의 오래된 서적과 C 매뉴얼 (70s / 80s)에서 얻을 수 있습니다. 따라서 마지막 '명령'은 부모 표현식에 사용되는 것입니다.

현대 C에서는 그 사용이 이상하지만 오래된 C (ANSI)에서는 매우 흥미 롭습니다.

do { 
    /* bla bla bla, consider conditional flow with several continue's */
} while ( prepAnything(), doSomethingElse(), logic_operation);

모든 연산 (함수)이 왼쪽에서 오른쪽으로 호출되지만 조건부 'while'의 결과로 마지막 식만 사용됩니다. 이것은 'goto'의 처리가 조건 점검 전에 실행되는 고유 한 명령 블록을 유지하는 것을 방지합니다.

편집 : 왼쪽 피연산자의 모든 논리를 처리 할 수있는 논리 함수를 호출하지 않으므로 논리 결과를 반환합니다. C의 과거에는 인라인 기능이 없었으므로 호출 오버 헤드를 피할 수 있습니다.


Luciano, 당신은이 답변에 대한 링크를 얻었습니다 : stackoverflow.com/questions/17902992/… .
gsamaras

인라인 함수 이전 90 년대 초에는 코드를 최적화하고 체계적으로 유지하기 위해 많은 기능을 사용했습니다.
Luciano
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.