5-3-나.쉼표 연산자

쉼표 연산자는 쉼표 기호(,)를 사용하는데 모양만으로 보면 구두점같이 생겨서 연산자가 아닌 것처럼 보이기도 한다. 하지만 분명히 연산자이다. 피연산자로 양쪽에 두 개의 표현식을 취하며 좌변을 먼저 평가하고 우변을 평가한 후 우변의 연산 결과를 리턴한다. 쉼표 연산자는 어떤 연산을 한다기보다는 두 연산식을 하나로 묶는 역할만 한다. 이 연산자를 사용하면 두 개의 표현식을 하나로 합칠 수 있다. 다음 예를 보자.

 

: comma

#include <Turboc.h>

 

void main()

{

     int i,j;

 

     i=3;

     j=i+2;

     printf("i=%d, j=%d\n",i,j);

}

 

i에 3을 먼저 대입하고 j에는 i에 2를 더한 값을 대입했다. 이 두 변수를 출력하면 i=3, j=5가 될 것이다. 너무 너무 쉽다. 만약 두 대입문을 하나로 합치고자 한다면 다음과 같이 쓸 수 있다.

 

j=(i=3,i+2);

 

좌변을 먼저 평가하므로 쉼표 연산자는 우선 i=3 대입문을 실행한다. 그리고 i+2를 평가한 후 그 결과를 리턴하는데 i가 먼저 3이 되었으므로 쉼표 연산문의 전체 결과는 3+2=5가 되어 j에 5가 대입될 것이다. 두 개의 대입식을 한 줄에 표현했다는 것 외에는 별다른 차이점이 없다. 대입 연산자로도 이 식을 압축할 수 있는데 j=(i=3)+2; 도 동일하다.

쉼표 연산자의 좌변은 단독으로 먼저 실행되기 때문에 독립적으로 의미가 있는 대입식이나 증감식 등이 와야 한다. 우변은 쉽표 연산 전체의 결과로 리턴되기 때문에 독립적이지 않은 상수나 수식이 와도 상관없다. 위 예에서 i=3은 독립적으로 동작하는 연산문이지만 i+2는 그 자체로는 어떤 동작도 하지 않으며 다른 변수에 대입될 때만 의미가 있다.

쉼표 연산자는 모든 연산자들 중에 우선 순위가 가장 늦다. 즉, 다른 연산자들과 함께 사용될 때 제일 늦게 연산된다는 뜻이다. 그래서 쉼표 연산자가 다른 연산자의 방해를 받지 않고 먼저 실행되려면 연산식 전체를 반드시 괄호로 싸야 한다. 만약 괄호를 생략해 버리면 완전히 딴 명령이 되어 버린다.

 

j=i=3,i+2;

 

이렇게 쓰면 j와 i는 3이 되고 i+2는 평가는 되지만 아무도 값을 대입받지 않으므로 버려진다. C는 수식이 단독으로 존재하는 것을 허용하기 때문에 에러는 아니지만 이 경우 i+2라는 연산문은 전혀 불필요한 명령이다.

그렇다면 쉼표 연산자는 어떤 때 사용할까? 단순히 두 문장을 하나로 합치기만 한다면 따로 쓰는 것과 별반 차이가 없다. 쉼표 연산자가 반드시 필요한 경우는 { }의 도움없이 두 개 이상의 문장을 하나로 묶어야 할 때와 for문에서 제어 변수 두 개를 사용하고자 할 때이다. 다음 예제를 실행해 보자.

 

: twocontrol

#include <Turboc.h>

 

void main()

{

     int i,j;

 

     for (i=1,j=1;i<5;i++,j+=2) {

          printf("i=%d",i);

          printf(",j=%d\n",j);

     }

}

 

실행 결과는 다음과 같다.

 

i=1,j=1

i=2,j=3

i=3,j=5

i=4,j=7

 

i는 1~4까지 변하되 이때 한 번 루프를 돌 때마다 j도 2부터 2씩 계속 증가하고 싶다면 쉼표 연산자를 사용하여 j에 대한 처리를 초기식과 증감식에 같이 포함시킬 수 있다. 두 개의 제어 변수가 동시에 진행되어야 하므로 중첩 루프와는 구조가 완전히 다르다. 초기식에 i=1 대입문과 j=2 대입문을 쉼표 연산자로 같이 묶어 두고 증감식에 i++과 j+=2를 같이 묶어 두면 된다. 초기식과 증감식에 반드시 쉼표 연산자가 필요한 이유는 여기에 { } 괄호로 명령 블록을 작성할 수 없기 때문이다. 만약 다음과 같은 표현이 가능하다면 굳이 쉼표 연산자를 쓸 필요가 없을 것이다.

 

for ({ i=1;j=2; };i<5;{ i++;j+=2; }) {

 

for문외에도 두 문장을 하나로 묶고 싶은 곳에는 언제든지 쉼표 연산자를 사용할 수 있다. 위 예제의 for 루프를 다음과 같이 수정해도 결과는 동일하다.

 

     for (i=1,j=2;i<5;i++,j+=2)

          printf("i=%d",i),printf(",j=%d\n",j);

 

두 개의 printf문을 쉼표 연산자로 연결했다. 두 문장이지만 하나로 묶였으므로 { }로 명령 블록을 구성하지 않아도 된다. 하지만 이 방법은 가능은 하지만 일반적이지 않으며 별로 바람직하지도 않다.

숫자 맞추기 게임인 RandNum 예제의 경우 선실행 후평가문인 do~while로 되어 있는데 입력을 받는 동작과 비교를 하는 연산을 콤마 연산자로 억지로 합치면 선평가 후실행문인 while로 바꿀 수 있다. while (scanf("%d",&input), input != num) 이라고 쓰면 두 문장이 하나로 합쳐진다. 좀 억지스러워 보이지만 어쩔 수 없이 이렇게 해야 하는 경우도 있을 수 있다.