쉼표 연산자는 무엇을합니까?


167

뭐라고합니까 ,연산자는 C에서 무엇입니까?



1
내 대답에서 알 수 있듯이 왼쪽 피연산자를 평가 한 후 시퀀스 포인트가 있습니다. 이것은 문법적인 함수 호출에서 쉼표와 다릅니다.
Shafik Yaghmour

2
@SergeyK. —이 질문은 다른 질문보다 몇 년 전에 요청되고 답변되었으므로 다른 질문이이 질문과 중복 될 가능성이 높습니다. 그러나 다른 하나는 cc ++ 모두로 이중 태그가 지정되어 성가신 것입니다. 괜찮은 답변이있는 C 전용 Q & A입니다.
Jonathan Leffler

답변:


129

표현식:

(expression1,  expression2)

먼저 expression1을 평가 한 다음 expression2를 평가하고 전체 표현식에 대해 expression2의 값을 리턴합니다.


3
그런 다음 i = (5,4,3,2,1,0)이라고 쓰면 이상적으로 0을 반환해야합니다. 맞습니까? 그러나 나는 5의 가치가 할당되고 있습니까? 내가 어디로 잘못 가고 있는지 이해하도록 도와 주시겠습니까?
Jayesh

19
@James : 쉼표 연산의 값은 항상 마지막 표현식의 값입니다. 어느 시점에서도 i5, 4, 3, 2 또는 1의 값을 가지지 않습니다 . 단순히 0입니다. 표현식에 부작용이 없으면 실제로는 쓸모가 없습니다.
Jeff Mercado

6
쉼표 식의 LHS 평가와 RHS 평가 사이에는 완전한 순서가 있습니다 ( C99 표준의 인용문 은 Shafik Yaghmour답변 참조 ). 이것은 쉼표 연산자의 중요한 속성입니다.
Jonathan Leffler 2016 년

5
i = b, c;(i = b), c할당 =이 쉼표 연산자보다 우선 순위가 높기 때문에 이는 동등합니다 ,. 쉼표 연산자는 우선 순위가 가장 낮습니다.
cyclaminist

1
괄호가 두 가지로 오해 될 우려가 있습니다. (1) 필요하지 않습니다. 쉼표 연산자를 괄호로 묶을 필요는 없습니다. (2) 함수 호출의 인수 목록 주위에 괄호와 혼동 될 수 있지만 인수 목록의 쉼표는 쉼표 연산자가 아닙니다. 그러나이를 수정하는 것이 결코 쉬운 것은 아닙니다. 아마도 : 성명서에서 : expression1, expression2;첫 번째 expression1는 아마도 그 부작용 (예 : 함수 호출)에 대해 expression2평가 된 후, 시퀀스 포인트가 있고, 평가되고 값이 반환됩니다.
Jonathan Leffler

119

while루프 에서 가장 많이 사용되는 것을 보았습니다 .

string s;
while(read_string(s), s.len() > 5)
{
   //do something
}

작업을 수행 한 다음 부작용을 기반으로 테스트를 수행합니다. 다른 방법은 다음과 같이하는 것입니다.

string s;
read_string(s);
while(s.len() > 5)
{
   //do something
   read_string(s);
}

21
이봐, 괜찮아! 나는 종종 그 문제를 해결하기 위해 루프에서 정통적인 일을해야했습니다.
staticsan September

6
다음과 같은 작업을 수행하면 덜 모호하고 읽기 쉽습니다 while (read_string(s) && s.len() > 5). read_string반환 값이 없거나 의미있는 값 이 없으면 분명히 작동 하지 않습니다. (편집 : 죄송합니다,이 게시물이 몇 살인지는 눈치 채지 못했습니다.)
jamesdlin

11
@staticsan 몸에 진술 을 사용 while (1)하는 것을 두려워하지 마십시오 break;. 코드의 브레이크 아웃 부분을 while 테스트 또는 do-while 테스트로 강제 실행하는 것은 종종 에너지 낭비이며 코드를 이해하기 어렵게 만듭니다.
potrzebie

8
@ jamesdlin ... 그리고 사람들은 여전히 ​​그것을 읽습니다. 쓸만한 말이 있다면 말하십시오. 스레드는 일반적으로 마지막 게시 날짜별로 정렬되므로 포럼에서 스레드를 다시 작성하는 데 문제가 있습니다. StackOverflow에는 이러한 문제가 없습니다.
Dimitar Slavchev

3
훨씬 더보다는 쉼표 방식처럼 I를 @potrzebie while(1)break;
Michael

38

콤마 연산자 왼쪽 피연산자를 평가하게는, 그 결과를 폐기하고 오른쪽 피연산자를 평가하고 그 결과가 될 것이다. 관용적 (A)에 사용되는 변수를 초기화 할 때 링크에서 언급 된 사용은 for루프 및 그 다음의 예이다 :

void rev(char *s, size_t len)
{
  char *first;
  for ( first = s, s += len - 1; s >= first; --s)
      /*^^^^^^^^^^^^^^^^^^^^^^^*/ 
      putchar(*s);
}

그렇지 않으면하지 많습니다 위대한 의 사용 쉼표 연산자는 그것을 읽고 유지하기 어려운 코드를 생성 할 수 남용이 용이하지만,.

로부터 초안 C99 표준에 다음과 같이 문법은 다음과 같습니다

expression:
  assignment-expression
  expression , assignment-expression

단락 2 는 다음 과 같이 말합니다.

쉼표 연산자왼쪽 피연산자는 void 표현식으로 평가됩니다. 평가 후 시퀀스 포인트가 있습니다. 그런 다음 올바른 피연산자가 평가됩니다. 결과 유형과 값이 있습니다. 97) 쉼표 연산자의 결과를 수정하거나 다음 시퀀스 포인트 이후에 액세스하려고하면 동작이 정의되지 않습니다.

각주 97의 말 :

쉼표 연산자는 lvalue를 생성 하지 않습니다 .

이는 쉼표 연산자 의 결과에 할당 할 수 없음을 의미합니다 .

쉼표 연산자가 우선 순위가장 낮 으므로 사용 ()이 큰 차이를 만들 수있는 경우가 있습니다. 예를 들면 다음과 같습니다.

#include <stdio.h>

int main()
{
    int x, y ;

    x = 1, 2 ;
    y = (3,4) ;

    printf( "%d %d\n", x, y ) ;
}

다음과 같은 출력이 나타납니다.

1 4

28

쉼표 연산자는 두 표현식을 한쪽으로 결합하여 왼쪽에서 오른쪽 순서로 평가합니다. 오른쪽의 값은 전체 표현식의 값으로 반환됩니다. (expr1, expr2)비슷 { expr1; expr2; }하지만 당신의 결과를 사용할 수있는 expr2함수 호출 또는 할당에.

다음 for과 같이 여러 변수를 초기화하거나 유지하기 위해 종종 루프에서 볼 수 있습니다 .

for (low = 0, high = MAXSIZE; low < high; low = newlow, high = newhigh)
{
    /* do something with low and high and put new values
       in newlow and newhigh */
}

이와는 별도로, 나는 항상 매크로에서 함께 진행해야하는 두 가지 작업을 마무리 할 때 다른 경우에는 "분노한"경우에만 사용했습니다. 우리는 네트워크에서 전송하기 위해 다양한 이진 값을 바이트 버퍼에 복사하는 코드가 있었고 포인터는 다음과 같은 위치에 유지되었습니다.

unsigned char outbuff[BUFFSIZE];
unsigned char *ptr = outbuff;

*ptr++ = first_byte_value;
*ptr++ = second_byte_value;

send_buff(outbuff, (int)(ptr - outbuff));

값이 shorts 또는 ints 인 경우 다음을 수행했습니다.

*((short *)ptr)++ = short_value;
*((int *)ptr)++ = int_value;

나중에 우리 (short *)ptr는 컴파일러가 신경 쓰지 않았지만 더 이상 l 값이 아니고 증가 할 수 없기 때문에 이것이 실제로 유효한 C가 아니라는 것을 읽었습니다 . 이 문제를 해결하기 위해 표현식을 두 가지로 나눕니다.

*(short *)ptr = short_value;
ptr += sizeof(short);

그러나이 접근법은 항상 두 진술을 모두 기억하는 모든 개발자에게 의존했습니다. 출력 포인터, 값 및 값 유형을 전달할 수있는 함수가 필요했습니다. 이것은 템플릿이있는 C ++이 아닌 C이므로 함수는 임의의 유형을 취할 수 없으므로 매크로를 설정했습니다.

#define ASSIGN_INCR(p, val, type)  ((*((type) *)(p) = (val)), (p) += sizeof(type))

쉼표 연산자를 사용하여 원하는대로 표현식이나 명령문으로 사용할 수있었습니다.

if (need_to_output_short)
    ASSIGN_INCR(ptr, short_value, short);

latest_pos = ASSIGN_INCR(ptr, int_value, int);

send_buff(outbuff, (int)(ASSIGN_INCR(ptr, last_value, int) - outbuff));

나는이 예제 중 어느 것이 좋은 스타일인지 제안하지 않습니다! 실제로 Steve McConnell의 Code Completefor루프 에서 쉼표 연산자를 사용하는 것에 대해서도 조언 하는 것을 기억 합니다. 가독성 및 유지 관리 성을 위해 루프는 하나의 변수로만 제어되어야하며 for행 자체 의 표현식 에는 루프 제어 코드 만 포함되어야합니다. 다른 여분의 초기화 또는 루프 유지 보수가 아닙니다.


감사! 그것은 StackOverflow에 대한 첫 번째 대답이었습니다. 그 이후로는 간결성이 중요하다는 것을 알게되었습니다 :-).
Paul Stephenson

때때로 나는 당신이 해결책의 진화를 묘사하는 곳 (여러분이 어떻게 도착했는지)에서와 같이 약간의 장황함을 중요하게 생각합니다.
green diod

8

여러 문을 평가하지만 마지막 문만 결과 값으로 사용합니다 (rvalue, 생각합니다).

그래서...

int f() { return 7; }
int g() { return 8; }

int x = (printf("assigning x"), f(), g() );

x가 8로 설정됩니다.


3

이전 답변에서 언급했듯이 모든 진술을 평가하지만 마지막 진술을 표현식의 값으로 사용합니다. 개인적으로 루프 표현식에서만 유용하다는 것을 알았습니다.

for (tmp=0, i = MAX; i > 0; i--)

2

내가 유용하다고 생각한 유일한 장소는 표현식 중 하나 (아마도 init 표현식 또는 루프 표현식)에서 여러 가지 작업을 수행하려는 펑키 루프를 작성할 때입니다.

bool arraysAreMirrored(int a1[], int a2[], size_t size)
{
  size_t i1, i2;
  for(i1 = 0, i2 = size - 1; i1 < size; i1++, i2--)
  {
    if(a1[i1] != a2[i2])
    {
      return false;
    }
  }

  return true;
}

구문 오류가 있거나 엄격한 C가 아닌 것을 혼합 한 경우 용서하십시오. 나는 연산자가 좋은 형식이라고 주장하지는 않지만 그것을 사용할 수 있습니다. 위의 경우 아마도 while루프를 대신 사용하므로 init 및 loop의 여러 표현식이 더 분명합니다. (그리고 선언하고 초기화하는 대신 i1 및 i2 인라인을 초기화했습니다 .... blah blah blah.)


나는 당신이 i1 = 0, i2 = size -1을 의미한다고 가정합니다
Frankster

-2

나는 이것이 @Rajesh와 @JeffMercado의 질문을 해결하기 위해 단순히 이것을 부활시키고 있습니다. 이것이 최고의 검색 엔진 히트 중 하나이기 때문에 매우 중요하다고 생각합니다.

예를 들어 다음 코드 스 니펫을 사용하십시오.

int i = (5,4,3,2,1);
int j;
j = 5,4,3,2,1;
printf("%d %d\n", i , j);

인쇄합니다

1 5

i사례는 대부분의 답변에서 설명한대로 처리됩니다. 모든 표현식은 왼쪽에서 오른쪽 순서로 평가되지만 마지막 표현식 만에 지정됩니다 i. ( ) is1` 의 결과 .

j보낸 경우에는 다른 우선 순위 규칙을 따르는 ,최저 연산자 우선 순위를 갖는다. 이러한 규칙으로 인해 컴파일러는 assignment-expression, constant, constant ...를 보게 됩니다 . 식은 다시 순서로 왼쪽에서 오른쪽 및 그 부작용이 항상 표시 따라서 평가되고 j있다 5의 결과 j = 5.

간혹 int j = 5,4,3,2,1;언어 사양에서 허용되지 않습니다. 초기화는 기대 할당 표현 직접적인 있도록 ,운영자가 허용되지 않습니다.

도움이 되었기를 바랍니다.

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