2009년 4월 24일 금요일

씹어먹는 C 언어 - <4. 계산하리 >

이번 강의에서는
  • 산술 연산자
  • 대입 연산자
  • 비트 연산자, 쉬프트 연산자
  • 변수에 관한 추가적인 내용
  • 산술 변환, 우선 순위
등을 배우게 됩니다. 



  안녕하세요 여러분. 지난 강의에서 모두들 변수에 대해 감이 잡혔을 것이라 믿고 강의를 진행하도록 하겠습니다.

  최초의 컴퓨터는 무엇을 하기 위해 태어났을까요? 오락용? 영화 시청? (물론 그 때에는 불가능 했을 터이지만). 아닙니다. 최초의 컴퓨터라 일컫어 지는 에니악(ENIAC.. 물론 에니악이 최초의 컴퓨터이냐 아니냐에 관한 논쟁은 길다. 한편에서는 콜로서스라는 주장도 있는데 아무튼) 은 포탄을 어떤 각도로 발사했을 때, 어디에 떨어질 지를 예측하는 기계였습니다. 단지, 계산 만 할 뿐. 그 후에서 수 많은 컴퓨터들이 나왔지만 20세기 후반 까지만 해도 컴퓨터는 단지 계산에 이용될 뿐이였습니다. 물론 지금도 컴퓨터의 가장 중요한 역할은 인간이 할 수 없는 복잡한 수식을 계산하는 것입니다. 다시말해, 컴퓨터는 '계산' 을 위해 태어난 기계인 것입니다.

 산술 연산자, 대입 연산자

 따라서, 이번 강의는 컴퓨터의 목적 달성 욕구를 채워주기 위한 강좌라 볼 수 있다. 컴퓨터의 역할에 걸맞게 C 프로그램을 통해 실컷 계산을 할 수 있게 해주자.


 일단, '계산' 이라 하면 머리속에 가장 먼저 떠오르는 것은 사칙연산, 즉 ,-,× ,÷ 을 가리킨다. 컴퓨터 상에서는 × 와 ÷ 기호를 쓰기 힘드므로, * 와 / 로 대체한다. 즉, 8 × 5 는 8 * 5 와 같은 것이고, 10 ÷ 7 은 10 / 7 과 같은 것이다. 또한, 색다른 연산자로 % 가 있는데 이는 나눈 나머지를 뜻한다. 예를들어 10 % 3 은 1 이 된다. 왜냐하면 10 을 3 으로 나눈 나머지가 1 이기 때문이다. 이러한 ,-,× ,÷ 들을 산술 연산자(Arithmetic Operator) 라고 합니다.

/* 산술 연산 */
#include <stdio.h>
int main()
{
    int a,b;
    a = 10;
    b = 3;
    printf("a + b 는 : %d \n", a + b);
    printf("a - b 는 : %d \n", a - b);
    printf("a * b 는 : %d \n", a * b);
    printf("a / b 는 : %d \n", a / b);
    printf("a %% b 는 : %d \n \n", a % b);

    printf("a / b 는 : %f \n", a / b); // 해서는 안될 짓
    return 0;
}

만약 위 코드를 잘 컴파일 했다면 아래와 같이 나온다. (컴파일 하는 방법을 까먹은 사람들은 1강을 참조하세요)


참고로, 맨 마지막 부분에 나오는 이상한 숫자는 저와 다를 수 있으니 걱정하지 마세요

a = 10;
b = 3;

  이러한 문장은 딱 봐도 한눈에 알 수 있다. a 의 값을 10, b 의 값을 3 이라 하는 것이다. 그런데 간혹가다가 위 문장을 아래와 같이 적는 사람도 있습니다.

10 = a;
3 = b;

  언뜻 보기에 맞는 문장인 것 같다. 왜냐하면, 실제 수학을 공부한 사람이라면 a = 10 이나 10 = a 나 별반 다를 것이 없기 때문이죠. 하지만, C 언어 컴파일러는 '=' 라는 기호를 뒤에서 부터 해석합니다. 즉, a = 10 은 '10 을 a 에 대입하라' 라는 문장이 되지만, 10 = a 는' a 의 값을 10 에 대입하라' 라는 이상한 문장이 되에 오류가 뜨는 것 입니다. 이렇게 '=' 를  유식한 말로 대입 연산자(Assignment Operator) 라고 합니다.  왜냐하면 우측의 값을 좌측에 '대입' 하는 것이기 때문이죠.

따라서,

a = 5;
b = 5;
c = 5;
d = 5;

라는 문장이나,

a = b = c = d = 5;

  라는 문장은 완전히 같은 것이 됩니다. 왜냐하면, 앞에서 말했듯이 = 는 뒤에서 부터 해석한다고 했으므로, 제일 먼저 d = 5 를 해석한 후, 그 다음에 c = d, b = c, a = b 로 차례대로 해석해 나가기 때문에 a = 5; b = 5; c = 5; d = 5; 라는 문장과 같은 것이지요.

    printf("a + b 는 : %d \n", a + b);
    printf("a - b 는 : %d \n", a - b);
    printf("a * b 는 : %d \n", a * b);
    printf("a / b 는 : %d \n", a / b);
    printf("a %% b 는 : %d \n \n", a % b);

  자, 이제 산술 연산자들에 대해 살펴보도록 합시다. 일단, 한 눈에 보게 a + b, a - b, a * b, a / b 는 각각 덧셈, 뺄셈, 곱셈, 나눗셈을 하여서 그 값이 %d 에 들어가 출력된 것 같습니다. 그런데, a + b, a - b, a * b 는 각각 계산 결과가 13, 7, 30 이 나온 사실을 쉽게 받아들일 수 있지만, a / b 가 왜 3 이 나왔는지는 이해하기 힘듭니다. 왜, a ÷ b 가 3 이 되었을 까요?

  사실, 3 강에서 말했지만 a 와 b 는 모두 int 형으로 선언된 변수 입니다. 즉, a 와 b 는 오직 '정수' 데이터만 담당합니다. 즉, a 와 b 는 모두 정수 데이터만 처리하기 때문에 a 를 b 로 나누면, 즉 10 을 3 으로 나누면 3.3333... 이 되겠지만 정수 부분인 3 만을 처리하게 되는 것 입니다. 따라서, 값은 3 이 출력됩니다.

  마지막으로 생소한 % 라는 연산자에 대해 살펴봅시다. +,-,*,/ 연산자는 모두 정수,실수형 데이터에 대해서 모두 연산이 가능한데, % 는 오직 정수형 데이터에서만 연산이 가능합니다. 왜냐하면 % 는 나눈 나머지를 표시하는 연산자 이기 때문이죠. a % b 는 a 를 b 로 나눈 나머지를 표시합니다. 즉, 10 % 3 = 1 이  되는 것이지요.

  이 때,

    printf("a %% b 는 : %d \n \n", a % b);

  %% 는 % 를 '표시'하기 위한 방법입니다. 왜냐하면 % 하나로는 %d , %f 같이 사용될 수 있기 때문이 표시가 되지 않습니다.

    printf("a / b 는 : %f \n", a / b); // 해서는 안될 짓

  위 프로그램을 실행 시켜 보신 분들은 알겠지만, 마지막 문장에서 엄청나게 긴 숫자가 튀어나오게 되죠. 물론, 그 숫자는 아무런 의미가 없는 숫자 입니다. 이러한 이상한 숫자가 출력된 이유는 바로 우리가 '해서는 안될 짓' 을 하였기 때문이죠. 3강 에서 우리는  %f 가 오직 실수형 데이터 만을 출력하기 위해 있는 것이라 하였습니다. 그런데, a / b 가 3.3333 이라고 해서 실수형 데이터가 되는 것이 아니라, (정수형 변수) (연산) (정수형 변수) 는 언제나 (정수) 이기 때문에 실수형 데이터를 출력하는 %f 를 이용하면 이와 같이 엄청난 오류가 뜨는 것 입니다.

  그렇다면 아래의 경우 어떻까요?

/* 산술 변환  */
#include <stdio.h>
int main()
{
    int a;
    double b;

    a = 10;
    b = 3;
    printf("a / b 는 : %f \n", a / b);
    printf("b / a 는 : %f \n", b / a);
    return 0;
}

만약 제대로 컴파일 했다면 아래와 같이 나오게 됩니다.


  a 는 정수형 변수, b 는 실수형 변수 입니다. 그런데, 이들에 대해 연산을 한 후에 결과를 실수형으로 출력하였는데 '정상적' 으로 나왔습니다. 그 것은 왜 일까요? 이는 컴파일러가 '산술 변환' 이라는 과정을 거치기 때문입니다. 즉, 어떠한 자료형이 다른 두 변수를 연산 할 때, 숫자의 범위가 큰 자료형으로 자료형들이 바뀝니다.
  즉, 위 그림에서도 보듯이 a 가 int 형 변수이고 b 가 double 형 변수인데, double 이 int 에 비해 포함하는 숫자가 더 크므로 큰 쪽으로 산술 변환됩니다. 일단, 정수형 변수와 실수형 변수가 만나면 무조건 실수형 변수쪽으로 상승되는데, 이는 실수형 변수의 수 범위가 최소 10
38 이상 되기 때문입니다. 위와 같은 산술 변환을 통해 애러가 없이 무사히 실행 될 수 있었습니다. 또한 double 형태로 산술 변환 되므로 결과도 double 형태로 나오기 때문에

printf(" a / b 는 : %d \n" , a / b);

와 같이 하면 오류가 생기게 됩니다. 왜냐하면 전에도 누누히 말했듯이 %d 는 정수형 값을 출력하는 방식이기 때문이죠.

/* 대입 연산자   */
#include <stdio.h>
int main()
{
    int a = 3;
    a = a + 3;
    printf("a 의 값은 : %d \n", a);
    return 0;
}

위 결과를 컴파일 하면 아래와 같이 나옵니다.


일단, 변수 선언 부분 부터 살펴 봅시다.


    int a = 3;

사실, 위 문장을 딱 보고 감이 바로 옵니다. "음... a 라는 변수를 선언하고 a 변수에 3 의 값을 집어 넣는구나" . 맞습니다. 사실 위 문장이나 아래 문장이나 다를 바가 없습니다.

int a;
a = 3;

  그냥, 타이핑 하기 귀찮아서 짧게 써 놓은 것 뿐입니다.

    a = a + 3;


그 다음 부분은 대입 연산자와 산술 연산자가 함께 나와 있군요. 만일, 우리가 방정식에 대해서 공부해 본 사람이라면 다음과 같이 이의를 제기할 수 도 있습니다.

a = a + 3
따라서 양변에서 a 를 빼면
0 = 3 ???

  물론, 위는 수학적으로 맞지만 C 언어 에서 의미하는 바는 다릅니다. 위에서 말했듯이, = 는 등호가 아닙니다. '대입' 연산자 입니다. 무엇을 대입하냐구요? 오른쪽의 값을 왼쪽으로 대입합니다. 즉, a + 3 의 값(6) 을 a 에 대입합니다. 따라서, a  =  6 이 되는 것이지요.

  이 때, 이와 같이 계산 될 수 있는 이유는 + 를 = 보다 먼저 연산하기 때문이죠. 즉, a + 3 을 먼저 한 후(+), 그 값을 대입(=) 하는 순서를 거치기 때문에 a 에 6 이라는 값이 들어갈 수 있게 됩니다. 이러한 것을 '연산자 우선순위' 라고 하는데, 밑에서 조금 있다가 다루어 보도록 하겠습니다.

/* 더하기 1 을 하는 방법  */
#include <stdio.h>
int main()
{
    int a = 1, b = 1, c = 1, d = 1;

    a = a + 1;
    printf("a : %d \n", a);
    b += 1;
    printf("b : %d \n", b);
    ++ c;
    printf("c : %d \n", c);
    d ++;
    printf("d : %d \n", d);

    return 0;
}

위 코드를 컴파일 하면 아래와 같이 나옵니다.


  음, 모두 2 가 되었군요. 사실 위에 나온 4 개의 코드는 더하기 1 을 한다는 점에서 모두 같습니다. 일단, 하나하나 차례대로 살펴봅시다.

    a = a + 1;

  가장, 기초적으로 1 을 더하는 방법입니다. 위 문장은 "a 에 a 에 1 을 더한 값을 대입한다. " 라는 뜻을 가지고 있죠?

    b += 1;

  이게 뭔가요! 처음 본 연산인 += 입니다. 이러한 연산을 복합 대입연산 이라 하며, b = b + 1 과 같습니다. 이렇게 쓰는 이유는 단지, b = b + 1 을 쓰기 귀찮아서 간략하게 쓰는 것입니다. 물론, b = b+1 과 b += 1 은 엄밀히 말하자면 같은 것은 아니지만 이에 대해서는 나중에 다루어 보도록 하겠습니다(우선 순위에서 약간 차이가 있습니다). 복합 대입 연산은 아래와 같이 여러 가지 형태로 이용될 수 있습니다.

b += x; // b = b + x; 와 같다
b -= x; // b = b - x;
와 같다
b *= x; // b = b * x; 와 같다
b /= x; // b = b / x; 와 같다

  마지막으로, 비슷하게 생긴 두 부분을 함께 살펴 보도록 하겠습니다.

    ++ c;
    d ++;


  위와 같은 연산자(++)를 증감 연산자라고 합니다. 둘 다, c 와 d 를 1 씩 증가시켜 줍니다. 그런데, ++ 의 위치가 다릅니다. 전자의 경우 ++ 이 피연산자(c) 앞에 있지만 후자의 경우 ++ 이 피연산자(d) 뒤에 있습니다. ++ 이 앞에 있는 것을 전위형 (prefix), ++ 이 뒤에 있는 것을 후위형(postfix) 라 하는데 이는 본질적으로 다릅니다. 전위형의 경우, 먼저 1 을 더해준 후 결과를 돌려주는데 반해, 후위형의 경우 결과를 돌려준 이후 1 을 더해줍니다. 사실, 이해가 잘 안될테니 아래를 보세요.

/* prefix, postfix  */
#include <stdio.h>
int main()
{
    int a = 1;

    printf("++a : %d \n", ++a);

    a=1;
    printf("a++ : %d \n", a++);
    printf("a : %d \n", a);

    return 0;
}

위 소스를 성공적으로 컴파일 했다면 아래와 같이 결과가 나온다.



 분명히, 위에서 ++c 나 d++ 이나 결과를 출력했을 때 에는 결과가 1 이 잘 더해져서 2 가 나왔는데 여기서는 왜 일까? 앞서 말했듯이 ++ a 는 먼저 1 을 더한 후 결과를 반환한다고 했고 a++ 은 먼저 결과를 반환 한 후, 1 을 더한 다고 했습니다.

    printf("++a : %d \n", ++a);

  즉, 위의 경우 a 에 먼저 1 을 더한 값인 2 를 printf 함수에 반환하여 %d 에 2 가 들어가게 됩니다. 그런데,

    printf("a++ : %d \n", a++);

  이 경우, 먼저 a 의 값을 printf 에 반환하며 %d 에 1 이란 값이 '먼저' 들어 간 후, 1 이 출력된 이후 a 에 1 이 더해집니다. 따라서, 다시 printf 문으로 a 의 값을 출력하였을 때 에는 2 라는 값이 나오게 되는 것입니다. 참고로, 위 4 개의 연산 중에서 가장 빨리 연산되는 것은 a++ 과 같은 증감 연산입니다(왜냐하면, a = a + 1 의 경우 ADD a  1 로 하지만, a++ 은 INC a 로 좀 더 빨리 계산된다. 자세한 내용은 나중에... ) 하지만, 요즈음의 컴파일러는 최적화가 잘 되어 있어, a = a + 1 같은 것은 a ++ 로 바꾸어 컴파일 해버립니다.

  비트 연산자

  마지막으로 생소한 연산자에 대해 알아보록 하겠습니다. 비트 연산자라고 부르는 이 것들은 정말 비트(bit) 를 가지고 연산을 합니다. 비트는 컴퓨터 기억 장치의 최소 단위로 1 비트는 0 과 1 만을 구분합니다. 이진법의 1 자리라 볼 수 있죠. 보통, 8개의 비트(8 bit) 를 묶어서 1 바이트(byte) 라고 하고, 이진법으로 8 자리 수라 볼 수 있죠. 따라서, 1 바이트로 나타낼 수 있는 수의 범위가 0 부터 11111111b 로 십진수로 바꾸면 0 부터 255 까지 나타낼 수 있습니다.
 
  비트연산자에는 & (And 연산), | (\ 위에 있는 것이다. 영문자 i 가 아닌다. Or 연산), ^ (XOR 연산), <<, >> (쉬프트 연산) , ~ (반전) 등이 있습니다. 일단, 각 연산자가 어떠한 역할을 하는지 살펴보도록 합시다.

AND 연산 (&)

AND 연산은 아래와 같은 규칙으로 연산된다. 

1 & 1 = 1
1 & 0 = 0
0 & 1 = 0
0 & 0 = 0


비트 연산은 각 자리를 연산하는데, 예를들어, 1010 & 0011 의 경우
위와 같이 한자리 한자리 각각 AND 연산하여, 위에 써 놓은 규칙대로 연산이 된다. 만약 두 숫자의 자리수가 맞지 않을 경우 ,예를들어 1111100 과 11 을 AND 연산 할 때 에는 11 앞에 0 을 추가하여 자리수를 맞추어 준다. 즉, 1111100 과 0000011 의 연산과 같습니다.

OR 연산 (|)

1 | 1 = 1
1 | 0 = 1
0 | 1 = 1
0 | 0 = 0

  OR 연산은 AND 연산과 대조적이다. 어느 하나만 1 이여도 모두 1 이 되는데, 예를들어 1101 | 1000 은 결과가 1101 이 됩니다.

XOR 연산 (^)

1 ^ 1 = 0
0 ^ 1 = 1
1 ^ 0 = 1
0 ^ 0 = 0


  XOR 연산은 특이하게도 두 수가 달라야지만 1 이 된다. 예를들어, 1100 ^ 1010 의 경우 결과가 0110 이 됩니다.

반전 연산(~)

  반전연산은 간단히 말에 0 을 1 로 1 을 0 으로 바꿔주는 것입니다. 예를들어서 ~ 1100 을 하면 그 결과는 0011 이 된다.

<< 연산 (쉬프트 연산)

위 연산 기호에서 볼 수 있듯이, 비트를 왼쪽으로 쉬프트(Shift) 시킨다. 예를 들어, 101011 를 1 만큼 쉬프트 시키면 ( 이를 a << 1 이라 나타냅니다)
  위 처럼 결과가 010110 이 됩니다. 이 때, << 쉬프트 시, 만일 앞에 쉬프트된 숫자가 갈 자리가 없다면, 그 부분은 버려집니다. 또한 뒤에서 새로 채워지는 부분은 앞에서 버려진 숫자가 가는 것이 아니라 무조건 0 으로 채워집니다.

>> 연산

 이는 위와 같은 종류로 이는 << 와 달리 오른쪽으로 쉬프트 해줍니다. 이 때, 오른쪽으로 쉬프트 하되, 그 숫자가 갈 자리가 없다면 그 숫자는 버려집니다. 이 때, 앞 부분에는 무조건 0 이 채워지게 되죠. 예를들어서 11100010 >> 3 = 0001110 이 됩니다.

비트 연산자를 자세히 다룬 이유는 이 부분이 약간 생소하기 때문입니다. 또한, 처음에 비트 연산자를 접할 때, '저런거 뭐에다 쓰지?' 라는 생각이 들기도 합니다. 그런데, 사실 비트 연산은 암호 분야에서 많이 쓰이며 쉬프트 연산도 가끔씩 유용하게 쓰입니다.

/* 비트 연산 */
#include <stdio.h>
int main()
{
    int a=0xAF; // 10101111
    int b=0xB5; // 10110101

    printf("%x \n", a & b); // a & b = 10100101
    printf("%x \n", a | b); //
a | b = 10111111
    printf("%x \n", a ^ b); // a ^ b = 00011010
    printf("%x \n", ~ a); // ~a = 1....1 01010000
    printf("%x \n", a << 2);// a << 2 = 1010111100
    printf("%x \n", b >> 3); // b >> 3 = 00010101

    return 0;
}

위를 성공적으로 컴파일 했다면


 위와 같이 나오게 됩니다. 일단, 첫 세줄은 그럭 저럭 이해가 잘 갑니다. 그런데, 네 번째 줄인 ~ a 연산에 대해 의문을 품는 사람들이 많습니다.

    printf("%x \n", ~ a); // ~a = 1....1 01010000

  우리의 기억을 되돌려 3 강으로 가 봅시다. 강의 중간쯤에 보면 여러가지 자료형 들에 대한 설명과 함께 작은 표가 있을 텐데 말이죠. 이를 다시 아래에 불러와 봅시다.


  int 형 변수에 대한 설명을 보니 옆에 'Size*' 이라 표시된 것이 있습니다. 이는 int 형 변수의 크기를 나타내는데 4 바이트라고 되어 있군요. 맞습니다. int 형 변수는 하나의 데이터를 저장하기 위하여 메모리 상의 4 바이트 - 즉 32 비트를 사용합니다. ( 1 byte = 8 bits) 아까, 하나의 비트가 0 과 1 을 나타낸다고 했으므로 (즉 1 개의 비트가 2 진수의 한 자리를 나타내게 되죠), 하나의 int 형 변수는 32 자리의 이진수라고 볼 수 있습니다. 예를들어 우리가 a = 1 이라 한 것은 실제로 컴퓨터에는 a =
00000000000000000000000000000001 이라 저장되는 것과 같게 되는 거죠.


  즉, 우리가 int a = 0xAF; 라고 한 것은 a = 10101111; 이 맞지만 사실 컴퓨터 메모리 상에서는 a 가 int 형이기 때문에 a = 00000000
000000000000000010101111 ( 10101111 앞에 0 이 24 개 있다 ) 이라 기억하는 것이 됩니다. 따라서, 이 숫자를 반전 시키게 되면
a = 11111111
111111111111111101010000, 즉 0xFFFFFF50 이 되는 것이지요. 마찬가지로 생각해 보면,

    printf("%x \n", a << 2);// a << 2 = 1010111100
    printf("%x \n", b >> 3); // b >> 3 = 00010101

  이 두 문장도 사실은 각각
00000000000000000000000010101111 과 00000000000000000000000010111111 을 쉬프트 연산한 것과 같습니다. 이 때, a 의 경우 00000000000000000000000010101111 을 왼쪽으로 2 칸 쉬프트 하면 00000000000000000000001010111100 이 되어 0x2BC 가 되고, b 의 경우 00000000000000000000000000010101 이 되어 0x16 이 됩니다.

복잡한 연산

마지막으로 여러 연산이 중첩된 혼합 연산에 대해 살펴 보도록 합시다. 우리가 연산을 하는데 에도 순서가 있듯이 컴퓨터에도 연산을 하는데 무엇을 먼저 연산을 할 지 우선 순위가 정해져 있을 뿐더러 연산 방향 까지도 정해져 있습니다. 이를 간단히 살펴 보자면 아래와 같습니다.

이와 같이 순위가 매겨져 있습니다. 수학에서 우리가 혼합 계산을 할 때, 소괄호 → 중괄호 → 대괄호 순으로 괄호를 써 주어야 되었었는데, C 언어에서는 소괄호 하나만으로 충분할 뿐더러 중괄호는 사용하면 안됩니다.

예를들어 수학에서 {a + b * (c + d) } / (23 * 12) 라고 했던 문장을 C 언어 에서는 그냥 (a + b * (c +d) ) / (23 * 12) 와 같이 소괄호 만으로 사용해도 괜찮습니다. 이 때, 눈여겨 보아야 할 점은 괄호들이 제 1 우선 순위에 위치하였다는 점 입니다. 따라서, 어떠한 연산이라도 괄호롤 싸 주게 되면 먼저 실행 됩니다.

위 결과를 토대로 아래의 연산식들이 어떠한 결과를 낳게 되는지 예측해 보세요

a = 1; b = 1;    a = a + a++;  b = b + ++b; // a,b 의 값은?

그 결과는 아래와 같습니다.

a = 3; b = 4;

왜 일까요? 일단, 연산자 우선순위를 먼저 살펴볼 필요성이 있습니다. 일단, ++ 이 2 위로 계산되고, 그 아래에 + 가 4위, 그 밑에 = 가 14 위로 계산됩니다. 즉, 컴퓨터는 ++ 을 가장 먼저 실행한다는 것이지요. 그렇게 따지면, 왜 a = a + a++ 과 b = b + ++b 가 차이가 생길까요?

우리가 컴퓨터라고 생각하고 각 계산 과정을 수행해 봅시다. 일단, a ++ 부터 살펴 봅시다.

a = a + a++;

이 때, a ++ 은 ++ 이 뒤에 있으므로 먼저 a 를 반환 한 후 ++ 을 해줍니다.
즉, 위 식은 아래와 같이 바뀌죠.

a= a + 1 (원래의  a 값 인 1 을 반환) , a = 2 (그다음에 ++ 을 해주어 2 가 됨) ;

따라서, a 가 2 가 된 상태에서

a = a + 1

을 실행해 주면 a = 2 + 1 = 3 이 되는 것이지요. 이제, 마찬가지 방법으로 아래의 식을 살펴 보도록 합시다.

b = b +  ++b;

  일단, ++ b 가 먼저 연산되는데 이 때, ++ b 는 ++ 을 먼저 실행 한 후 그 결과를 반환해 주므로, b = b + 2, b = 2 가 되는 것 입니다. 따라서,

b = 2 + 2 = 4

가 되는 것이지요. 마지막으로, 결합 순위에 대해 잠시 다루어 보도록 하겠습니다. 표의 오른쪽을 보면 결합 순위가 나와 있는데, 대부분이 '왼쪽 우선' 이지만 몇 개는 '오른쪽 우선' 입니다. 이 말이 뜻하는 바가 무엇이냐면, 아래와 같은 문장을 수행할 때 계산하는 순위를 이야기 합니다.

a = b + c + d +e;

  위 표에서 보듯이, 덧셈의 결합 순서가 왼쪽 우선이므로 위 계산과정은 아래의 순서대로 진행됩니다.

b + c 를 계산하고 그 결과를 반환( 그 결과를 C 라 하면)
C + d 를 계산하고 그 결과를 반환( 그 결과를 D 라 하면)
D + e 를 계산하고 그 결과를 반환(그 결과를 E 라 하면)

 따라서, 위 식은

a = E

  가 되죠. 따라서, a 에 E 의 값, 즉 b + c + d + e 의 값이 들어가게 됩니다.

또한, 위 표에서 몇 안되는 '오른쪽이 우선' 인 대입 연산자(=) 를 살펴봅시다. 만약 대입 연산자가 왼쪽 우선이였다면 아래의 식이 어떻게 계산될 지 생각해 봅시다.

a = b =  c = d = 3;

만약 왼쪽 우선이였다면 a = b; b = c; c = d; d = 3 의 형식으로 계산되어 a , b, c 에는 알 수 없는 값이 들어가겠죠. 하지만 오른쪽이 우선이므로 위 식은 d = 3, c = d, b = c, a = b 의 형식으로 계산되어 a,b,c,d 의 값이 모두 3 이 될 수 있었습니다. 

자, 이제 연산자에 대한 강의가 끝났습니다. 연산자는 C 언어에서 가장 기초적인 부분이라 할 수 있습니다. 마치 수학에서 덧셈, 뺄셈을 가장 처음에 배우는 것 처럼 말이죠. 이번 강좌에서는 특별히 예제를 많이 만들어 보지는 않았지만 여러분 께서 C 언어를 통해 복잡한 수식의 계산을 하거나, 복잡한 수식을 보고 이러한 연산은 이 순서로 연산될 것이다 라고 예측해 보는 것도 우선순위를 이해하는데 도움이 될 것입니다. 보통, 우선순위를 잘못 고려하여 나는 오류들은 찾기가 매우 힘들기 때문에 우선순위를 빠삭하게 잘 이해하는 것이 좋습니다.







댓글 2개:

  1. 우선 순위는 코딩 습관으로 간편하게 해결 가능한 것 같더군요.

    * / % 는 + - 보다 우선하고 나머지는 ( )로 묶어주면 원하는 방향으로 계산을 이끌어 갈 수 있더군요.

    다만 ( )로가 너무 복잡해서 햇갈리기 시작하면 어쩔수 없지만요. 그럴때는 변수를 늘려서

    알기 쉽게 정리하는 것이 좋을 것 같습니다.

    답글삭제
  2. @거북이 - 2009/05/01 00:18
    괄호를 쓰면 좋지만, 코딩할 때 괜히 귀찮아서 안 쓰는 경우도 있지요. ㅎㅎ

    답글삭제