쉼표 연산자는 어떻게 작동합니까


175

쉼표 연산자는 C ++에서 어떻게 작동합니까?

예를 들어 내가하면 :

a = b, c;  

결국 b 또는 c와 같습니까?

(예, 이것이 테스트하기 쉽다는 것을 알고 있습니다-누군가가 대답을 빨리 찾을 수 있도록 여기에 문서화하십시오.)

업데이트 : 이 질문은 쉼표 연산자를 사용할 때 뉘앙스를 드러 냈습니다. 이것을 문서화하기 만하면됩니다.

a = b, c;    // a is set to the value of b!

a = (b, c);  // a is set to the value of c!

이 질문은 실제로 코드의 오타에서 영감을 얻었습니다. 의도 된 것

a = b;
c = d;

로 전환

a = b,    //  <-  Note comma typo!
c = d;

자세한 내용은 여기를 참조하십시오. stackoverflow.com/questions/12824378/…
코딩 매쉬

1
의 가능한 중복 `C에, 쉼표 연산자`무엇을합니까? . 그것은 하루에 당신을 이겼습니다. 그리고 lillq의 답변은에 관한 질문에 대한 답변을 제공합니다 a = (b, c);.
jww

5
그러나이 경우 a = b, c = d;실제로 의도 한 것과 동일한 성능을 발휘 a = b; c = d;합니까?
Bondolin

@NargothBond 반드시 그런 것은 아닙니다. 경우 bd함수 사용 (수정) 것으로 평가 일반적인 상태이며, 실행 순서는까지 정의되지 않는다 C++17.
Nyronium

답변:



129

쉼표 연산자가 C ++에서 오버로드 될 수 있다는 점에 유의하십시오. 따라서 실제 동작은 예상 한 것과 매우 다를 수 있습니다.

예를 들어, Boost.Spirit 는 쉼표 연산자를 사용하여 심볼 테이블에 대한 목록 이니셜 라이저를 구현합니다. 따라서 다음 구문이 가능하고 의미가 있습니다.

keywords = "and", "or", "not", "xor";

연산자 우선 순위로 인해 코드는 (의도적으로!) 동일합니다.

(((keywords = "and"), "or"), "not"), "xor";

즉, 첫 번째 호출 keywords.operator =("and")되는 연산자 는 나머지 operator,s가 호출 되는 프록시 객체를 반환합니다 .

keywords.operator =("and").operator ,("or").operator ,("not").operator ,("xor");

음, 우선 순위를 변경할 수는 없습니다. 즉, 목록 주위에 괄호를 넣어야합니다.
Jeff Burdges

18
@Jeff 반대로. 목록 주위에 괄호를 사용하면 컴파일러가 작동 char[]하지 않으므로 오버로드 할 수없는 두 사이의 쉼표 연산자 만 볼 수 있습니다. 코드는 의도적으로 먼저를 호출 operator=한 다음 operator,나머지 각 요소에 대해 호출합니다 .
Konrad Rudolph

125

쉼표 연산자는 모든 C / C ++ 연산자 우선 순위가 가장 낮습니다 . 따라서 항상 표현식에 바인딩하는 것은 마지막 방법입니다.

a = b, c;

다음과 같습니다.

(a = b), c;

또 다른 흥미로운 사실은 쉼표 연산자가 시퀀스 포인트를 도입한다는 것입니다 . 이것은 다음과 같은 표현을 의미합니다.

a+b, c(), d

세 개의 하위 표현식 ( a + b , c ()d )을 순서대로 평가해야합니다. 부작용이있는 경우 중요합니다. 일반적으로 컴파일러는 원하는 순서대로 하위 표현식을 평가할 수 있습니다. 예를 들어 함수 호출에서 :

someFunc(arg1, arg2, arg3)

인수는 임의의 순서로 평가할 수 있습니다. 함수 호출의 쉼표는 연산자 가 아닙니다 . 그것들은 구분자입니다.


15
,우선 순위가 낮다는 점을 지적 할 가치가 있지만, 그 자체 보다 뒤떨어 집니다 .) ... 쉼표 연산자 는 쉼표 구분 기호 보다 우선 순위가 낮습니다 . 따라서 단일 함수 인수, 변수 할당 또는 쉼표로 구분 된 목록 내에서 쉼표로 연산자 를 사용하려면 다음과 같이 괄호를 사용해야합니다.int a = 1, b = 2, weirdVariable = (++a, b), d = 4;
underscore_d

68

쉼표 연산자 :

  • 우선 순위가 가장 낮다
  • 좌파 연합

쉼표 연산자의 기본 버전은 모든 유형 (내장 및 사용자 정의)에 대해 정의되며 다음과 같이 작동합니다 exprA , exprB.

  • exprA 평가된다
  • 의 결과 exprA는 무시된다
  • exprB 평가된다
  • 결과 exprB전체 식의 결과로 반환됩니다

대부분의 연산자를 사용하면 컴파일러에서 실행 순서를 선택할 수 있으며 최종 결과에 영향을 미치지 않으면 실행을 건너 뛰어야합니다 (예 :에 false && foo()대한 호출을 건너 뜁니다 foo). 그러나 이것은 쉼표 연산자에는 해당되지 않으며 위의 단계는 항상 발생합니다 * .

실제로 기본 쉼표 연산자는 세미콜론과 거의 같은 방식으로 작동합니다. 차이점은 세미콜론으로 구분 된 두 표현식은 두 개의 별도 명령문을 형성하지만 쉼표로 구분하면 모두 단일 표현식으로 유지됩니다. 다음과 같은 시나리오에서 쉼표 연산자가 사용되는 이유가 있습니다.

  • C 구문에는 문장이 아닌 단일 표현식이 필요합니다 . 예를 들어if( HERE )
  • C 구문에는 for루프를 초기화 할 때 하나 이상의 문장이 필요합니다.for ( HERE ; ; )
  • 중괄호를 건너 뛰고 단일 문장을 유지하려면 : if (foo) HERE ;그렇게하지 마십시오. 정말 못생긴 것입니다!

명령문이 표현식이 아닌 경우 세미콜론을 쉼표로 바꿀 수 없습니다. 예를 들어 다음은 허용되지 않습니다.

  • (foo, if (foo) bar)( if표현이 아닙니다)
  • int x, int y (변수 선언은 표현식이 아닙니다)

귀하의 경우에는

  • a=b, c;, 등가 a=b; c;, 그 가정은 a콤마 연산자 과부하하지 않는 타입이다.
  • a = b, c = d;동등 a=b; c=d;그 가정은 a콤마 연산자 과부하하지 않는 타입이다.

모든 쉼표가 실제로 쉼표 연산자 인 것은 아닙니다. 완전히 다른 의미를 가진 일부 쉼표 :

  • int a, b; --- 변수 선언 목록은 쉼표로 구분되지만 쉼표 연산자는 아닙니다
  • int a=5, b=3; --- 이것은 쉼표로 구분 된 변수 선언 목록이기도합니다.
  • foo(x,y)--- 쉼표로 구분 된 인수 목록. 사실, xy에서 평가 될 수 있는 위해!
  • FOO(x,y) --- 쉼표로 구분 된 매크로 인수 목록
  • foo<a,b> --- 쉼표로 구분 된 템플릿 인수 목록
  • int foo(int a, int b) --- 쉼표로 구분 된 매개 변수 목록
  • Foo::Foo() : a(5), b(3) {} --- 클래스 생성자의 쉼표로 구분 된 이니셜 라이저 목록

* 최적화를 적용하는 경우에는 전적으로 사실이 아닙니다. 컴파일러가 특정 코드 조각이 나머지 코드에 전혀 영향을 미치지 않는다는 것을 인식하면 불필요한 명령문을 제거합니다.

더 읽을 거리 : http://en.wikipedia.org/wiki/Comma_operator


그것은이 경우 지적 가치가 operator ,과부하 (당신의 단락 속성을 잃게처럼 당신이 연관성에 어떤 보장을 잃게 operator&&하고 operator||그들이 오버로드 된 경우)?
YoungJohn

쉼표 연산자는 과부하 여부에 관계없이 왼쪽 연결입니다. 표현은 a, b, c항상 의미 (a, b), c하며 결코 아닙니다 a, (b, c). 요소의 유형이 다른 경우 후자의 해석으로 인해 컴파일 오류가 발생할 수도 있습니다. 이후 해석은 인수 평가 순서입니까? 나는 그것에 대해 확신하지 못하지만 아마도 당신이 옳습니다 : 쉼표가 왼쪽으로 연결되어 있어도 전에c 평가 될 수 있습니다. (a, b)
CygnusX1

1
클래스 생성자에서 쉼표로 구분 된 초기화 목록에 대한 약간의 주석 만, 순서는 목록의 위치에 따라 결정 되지 않습니다 . 순서는 클래스의 선언 위치에 따라 결정됩니다. 예를 struct Foo { Foo() : a(5), b(3) {} int b; int a; }들어 b(3)전에 증발 a(5)합니다. 목록이 다음과 같은 경우에 중요합니다 Foo() : a(5), b(a) {}.. b는 5로 설정되지 않지만 컴파일러가 경고하거나 경고하지 않을 수도있는 초기화되지 않은 a의 값입니다.
Jonathan Gawrych

최근에 두 개의 부동 소수점이있는 쉼표 연산자를 발견했습니다. 숫자를 평가하고 버리는 요점은 무엇입니까?
Aaron Franke

아무도 대답 할 수 없다고 생각합니다. 상황에 맞게 표시해야합니다. 아마도 별도의 질문입니까?
CygnusX1

38

의 값은 a됩니다 b만, 값 표현이 될 것입니다 c. 즉

d = (a = b, c);

A와 동일한 것 b, 그리고 d동일한 것이다 c.


19
거의 맞습니다. 문장에는 값이없고 표현식에는 없습니다. 해당 표현식의 값은 c입니다.
Leon Timmermans

왜 이것이 대신 사용 a = b; d = c;됩니까?
Aaron Franke

이를 통해 사람들이 무슨 부작용을 이야기했는지 이해할 수있었습니다.
신발 끈

8

b의 값은 a에 할당됩니다. C에 아무 일도 일어나지 않을 것이다



2

예 쉼표 연산자는 할당 연산자보다 우선 순위가 낮습니다.

#include<stdio.h>
int main()
{
          int i;
          i = (1,2,3);
          printf("i:%d\n",i);
          return 0;
}

출력 : i = 3
쉼표 연산자는 항상 가장 오른쪽 값을 반환하기 때문입니다.
대입 연산자가있는 쉼표 연산자의 경우 :

 int main()
{
      int i;
      i = 1,2,3;
      printf("i:%d\n",i);
      return 0;
}

출력 : i = 1
아시다시피 쉼표 연산자는 할당보다 우선 순위가 낮습니다 .....


그렇다면 두 번째 예 i = 1;는 그 선과 다른 점은 무엇입니까?
Aaron Franke

-3

먼저 쉼표는 실제로 연산자가 아니며 컴파일러의 경우 다른 토큰과 관련 하여 의미를 얻는 토큰 일뿐 입니다.

이것은 무엇을 의미하며 왜 귀찮게합니까?

예 1 :

다른 상황에서 동일한 토큰의 의미의 차이점을 이해하기 위해이 예제를 살펴 봅니다.

class Example {
   Foo<int, char*> ContentA;
}

보통 C ++ 초보자는이 표현이 / 일을 비교 할 수 있다고 생각하지만, 절대적으로 잘못의 의미 <, >그리고 ,사용의 맥락에 의존했다 토큰.

위 예제의 올바른 해석은 물론 템플릿에 대한 설명입니다.

예 2 :

루프를 반복 할 때마다 수행해야하는 초기화 변수 및 / 또는 둘 이상의 표현식으로 일반적으로 for 루프를 작성할 때 쉼표도 사용합니다.

for(a=5,b=0;a<42;a++,b--)
   ...

쉼표의 의미는 사용 상황에 따라 다르며 여기에서 for구성 의 상황이 됩니다.

문맥 상 쉼표 란 실제로 무엇을 의미합니까?

C ++에서와 같이 더 복잡하게하기 위해 쉼표 연산자 자체가 과부하 될 수 있습니다 ( Konrad Rudolph 덕분에) ).

질문으로 돌아 가기 위해 강령

a = b, c;

컴파일러를위한 수단

(a = b), c;

때문에 우선 순위=토큰 / 연산자의 우선 순위보다 높은 ,토큰.

이것은 다음과 같은 맥락에서 해석됩니다.

a = b;
c;

(해석은 컨텍스트에 따라 달라지며 여기서는 함수 / 메서드 호출이나 템플릿 인스톨레이션이 아닙니다.)


1
확실히 그것은 어쩌면 내가 (렉서 것은 물론, 토큰입니다) 잘못된 용어를 사용한다
Quonux

2
연산자 (sic) 와 함께 작동하므로 쉼표는 실제로 연산자입니다.
DragonLord

2
주어진 쉼표 토큰이 쉼표 연산자로 인식되는지 인식하지만 (예를 들어 인수 구분 기호가 아닌) 자체적으로 문제가 될 수 있지만이 질문은 특히 쉼표 연산자 에 관한 것 입니다.
CygnusX1
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.