IT Japan

제 4장 - 연 산 자 본문

IT/Programming

제 4장 - 연 산 자

swhwang 2016. 3. 22. 00:10
반응형


이번 장에서는 프로그램에서 연산을 가능하게 만들어 주는 연산자들에 관 

하여 알아 보겠다. 

연산자란 대상 자료에 대해 어떤 조작을 하는 부호를 말하며, 이 때 대상 

이 되는 자료들을 오퍼랜드(Operand)라고 부른다. 이러한 연산의 대상이 

되는 오퍼랜드의 갯수에 따라 C의 연산자는 1항 연산자, 2항 연산자, 3항 

연산자 등으로 구분할 수 있다. 

또한 각 연산자들은 우선 순위라는 게 있어 한 문장에서 여럿의 연산자들 

이 쓰였을 때 그 우선순위에 의거하여 연산을 수행하게 된다. 그리고 동일 

한 우선순위의 연산자들이 한 수식에 동시에 쓰였을 경우에는, 이 식을 좌 

측부터 평가하느냐 우측부터 평가하느냐에 따라 좌결합성과 우결합성으로 

나누어 지는 성질을 가지는 것도 있다. 

위에서 언급한 연산자의 특징들이 초보자들에게는 혼란스럽겠지만, 일단 

익혀두면 복잡한 알고리즘도 쉽고 함축성 있게 표현할 수 있어 유용하다. 


<표1> 각 연산자들의 종류와 우선 순위 

+-----------+-------+-----------------------------------+----+ 

| 대 분 류 |소분류 | 연 산 자 |결합| 

| | | |규칙| 

+===========+=======+===================================+====+높다 

| 일 차 식 |primery| ( ) [ ] -> . | -> | 

+-----------+-------+-----------------------------------+----+ 

|단항 연산자| 단 항 | ! ~ ++ -- - cast연산자 * & sizeof | <- | 

+-----------+-------+-----------------------------------+----+ 

| | 승 제 | * / % | -> | 

| |-------+-----------------------------------+----+ 

| | 가 감 | + - | -> | 우 

| |-------+-----------------------------------+----+ 

| |쉬프트 | << >> | -> | 

| |-------+-----------------------------------+----+ 

| | 비 교 | < <= > >= | -> | 선 

| |-------+-----------------------------------+----+ 

|이항 연산자| 등 가 | == != | -> | 

| |-------+-----------------------------------+----+ 

| |비트AND| & | -> | 

| |-------+-----------------------------------+----+ 

| |비트XOR| ^ | -> | 순 

| |-------+-----------------------------------+----+ 

| |비트 OR| | | -> | 

| |-------+-----------------------------------+----+ 

| |논리AND| && | -> | 위 

| |-------+-----------------------------------+----+ 

| |논리 OR| || | -> | 

+-----------+-------+-----------------------------------+----+ 

|삼항 연산자| 조 건 | ? : | <- | 

+-----------+-------+-----------------------------------+----+ 

| 치 환 | |=+= -= *= /= %= | | 

| | 치 환 |-----------------------------------| <- | 

| 연 산 자 | | >>= <<= &= ^= != | | 

+-----------+-------+-----------------------------------+----+ 

|순차 연산자| 순 차 | , | -> |낮다 

+-----------+-------+-----------------------------------+----+ 


1. 산술 연산자 


+ : 오른쪽에 있는 값을 왼쪽에 있는 값에 더한다. 

- : 왼쪽에 있는 값에서 오른쪽의 값을 뺀다. 

* : 오른쪽의 수를 왼쪽의 수로 곱한다. 

/ : 왼쪽의 수를 오른쪽의 수로 나눈다. 정수와 정수의 연산일 땐,소수점 

이하는 버린다. 

% : 왼쪽의 수를 오른쪽의 수로 나눌때 나머지를 결과로 취한다. (정수에 

서만 사용) 

++ : 오른쪽이나 왼쪽에 있는 변수의 값을 하나 증가시킨다. 

-- : 오른쪽이나 왼쪽에 있는 변수의 값을 하나 감소시킨다. 


그러면, ++와 --연산자에 사용에 대한 예를 들어보자. 


<예제1> ++,-- 연산자의 사용 


<리스트1> #include 

main() 

int a=1, b=1; 

int aplus, plusb; 


aplus=a++; 

plusb=++b; 

printf(" A Aplus B plusB\n"); 

printf(" %3d %5d %5d %5d\n",a,aplus,b,plusb); 

getch(); 


프로그램의 실행결과는 a가 2, aplus가 1, b가 2, plusb가 2가 된다. 

애매하지만 아래를 보자. 


aplus=a++; 후위형 : a가 사용된 후, a의 값이 하나 증가한다. 

plusb=++b; 전위형 : b의 값이 하나 증가한 후, b가 사용된다. 


그러면, while (num<21) { /* num이 5일때, 어떤 값이 나올까? */ 

printf(%d %d\n", num, num*num++); 


이 식을 보면, 5 25를 찍고, num을 6으로 증가시키는 자연스러운 식처럼 

보이나, 실제로 실행결과는 6 25가 찍힌다. 왜냐하면 printf()함수는 마 

지막 인자부터 먼저 넘긴다는 오묘한 진리가 있다. 


ans=num/2 + 5*(1 + num++); 


보통, num/2가 먼저 계산이 된다고 믿고 싶으나, 실제로는 먼저 num이 증 

가하고 나서 num/2에 사용된다. () 때문이다. 


위의 여러가지 예에서 보듯이 ++/--연산자의 전위와 후위의 차이점을 명 

확히 이해하고 이 연산자를 너무 남용하거나 복잡하게 사용하지 말자는 

뜻에서 함정을 미리 파 헤쳐 보았다. 



2. 치환 연산자 ( +=, -=, *=, /=, %=, &=, |=, =, >>=, <<= ..) 


대입 연산자라고 하는 이러한 연산자들은 모두 =와 결합되어서 식의 복잡 

성을 줄여준다. 

x += 3; 은 x=x + 3;과 같다. 

x *= y + 1; 은 x=x * (y + 1);과 같다. 

x += y += z=1; 은 z=1; y=y+z; x=x+y와 같다. 


치환은 a=b;처럼 단순치환일 수도 있고, a=b=c=d; 처럼 다중치환일 수도 

있으며, a += b처럼 복합치환일 수 있다. 

다중 치환의 경우에는 식을 우측에서 평가하여 좌측의 변수로 차례대로 

치환하게 된다.( 표1참조 - 우결합성 ) 

a=b=c=d=1;은 

a=( b=( c=( d=1 )));과 같으며, d=1;c=d;b=c;a=b;와 같다. 

a=b*c=d*e=1;은 e=1; c=d*e; a=b*c;과 같다. 



3. 관계 연산자 ( <, >, <=, >=, ==, != ) 



이 연산자들은 자료의 크기 등을 판단할 때 사용하며, 식의 값이 참이면 

1, 거짓이면 0의 값을 갖는다. 그리고 <=, >=는 부호의 순서가 바뀌면 안 

된다. 그리고, 관계 연산자들 중 ==, !=는 다른 관계 연산자들보다 우선순 

위가 낮다. 그리고 초보자들은 ==을 쓴다는 게 그만 =(대입연산자)를 쓰는 

경우가 있다. 이러한 것은 오류를 찾기에도 힘들 뿐 아니라 실행 결과에도 

심각한 영향을 미친다. ==와 =는 엄연히 다르므로 기억하자! 6.25! (?) 


a < b < c 의 평가 과정 


1) a < b 이면 참(1)이므로 1(a 2) a < b 가 아니면 거짓(0)이 되므로 0(a>=b) 그 외의 경우는 모두 0이다. 


x > y + 2 는 관계 연산자들의 우선순위가 산술 연산자들보다 낮으므로 

( x > y ) + 2 가 아니라, x > (y+2) 와 같다. 


그리고 관계 연산자들은 좌결합성을 가지므로 ex != wye == zee 는 실제 

로는 ( ex != wye ) == zee;와 같다. 


위에서 보았듯이 처음에는 그냥 지나치기 쉬우나, 좌결합성이냐 우결합성 

이냐와 연산자들의 우선 순위는 중요하므로 잘 알아두자. 만일 마냥 헷갈 

린다면, ( )를 십분 활용하여 에러의 소지를 없앨 필요가 있다. 



4. 논리 연산자 ( !, &&, || ) 


(1) 논리부정 연산자(NOT) - 식의 값이 0(거짓)이면 '!식'의 값은 1(참) 

이 되고 식의 값이 참이면 거짓이 된다. 

(2) 논리곱 연산자(AND &&) - 여러 식중 한개라도 거짓의 값이 있으면 거 

짓이 된다. 

(3) 논리합 연산자(OR ||) - 여러 식중 한개라도 참이면 참의 값을 가지 

게 된다. 


(cat > rat) && ( debt == 100 ) 은 두 식이 모두 참이어야 참이 된다. 

(cat > rat) || ( debt == 100 ) 은 둘 중 하나만 참이어도 참이 된다. 


5>2 && 4>7 은 하나만 참이므로 거짓이다. 

5>2 || 4>7 은 둘 중 하나가 참이므로 참이 된다. 

!(4>7)은 4>7이 거짓이므로 참이 된다. 


<예제2> x에 관한 여러 수식이 주어져 있다. 각 식의 x의 값이 어떻게 수 

행 되는 지 예측해 보라. 


<리스트2> 1: #include 

2: main() 

3: { 

4: int x, y, z; 

5: x=1, y=2, z=4; 

6: x=x || y && z; 

7: printf("%d\n",x); 

8: x=1, y=2, z=3; 

9: x=(x > y || z ==y && x 10: printf("%d\n",x); 

11: x=y=z=1; 

12: x=-y++ + ++z; 

13: printf("%d\n%d\n%d\n",x,y,z); 

14: } 


우선, 위 프로그램의 실행결과는 1 0 1 2 2이다. 예상결과와 맞았는가? 

다르다면, 아래의 설명을 잘 읽어보자. 

4,5,6행에서 6행은 우선순위에 의거하면 x=(x ||(y && z)); 로 괄호를 

칠 수가 있다. 여기에 수를 대입해 보면, x=(1 ||(2 && 3))=(1 || 1)=1이 된다. (C에서는 식의 값이 0이 아닐 때는 모두 참으로 간주한다) 

8,9,10행에서 9행을 우선순위에 의거하여 괄호를 쳐 보면, x=(x > y)|| 

(z == y) && (x < z))와 같다. 여기에 수를 대입해 보면 결과는 0이 된다. 

11,12,13행에서 12행은 x=-( y++ ) + ( ++z )=-(1)+(2) 로서 x=1, y= 

2, z=2가 된다. 


그러면, 아래를 보자. 


k=a && b++; 의 식은 어떻게 평가가 될까? 

1) k=a가 거짓일 때: b의 값은 변하지 않는다. 

2) k=a가 참일 때: b의 값은 1만큼 증가한다. 

=======> 대강 감이 오리라고 믿는다. 



5. 비트 연산자 


비트 연산자란 비트조작을 위해 쓰이는 연산자이다. 주의할 점은 오퍼랜 

드는 정수 또는 정수형이라야 한다. 


(1) 비트합 연산자(OR- |) : 좌우의 식을 이진수로 하는 비트합을 구해준 

다. 

a=117, b=216일 때 a|b의 결과 

117 => 0000 0000 0111 0101 

216 => 0000 0000 1101 1000 

============================ 

0000 0000 1111 1101 => 253 


비트합 연산자의 용도는 바로 특정한 비트를 1로 세트시킬때 이용된다. 


a=117때 상위 4비트를 1로 한다. 

117 => 0000 0000 0111 0101 

1111 0000 0000 0000 

============================ 

1111 0000 0111 0101 => 1로 세트하려는 부분만 0으로 


(2) 비트곱 연산자(AND- &) : 좌우의 식을 2진수로 하는 비트곱을 구해준 

다. 


a=117, b=216일 때 a&b의 결과 

117 => 0000 0000 0111 0101 

216 => 0000 0000 1101 1000 

============================ 

0000 0000 0101 0000 => 80 


비트곱 연산자의 용도는 특정 비트를 0의로 리셋하려는 경우에 이용된다. 


a=44149일 때 상위 4비트를 0으로 한다. 

44149 => 1010 1100 0111 0101 

0000 1111 1111 1111 

============================== 

0000 1100 0111 0101 => 0으로 리셋하려는 부분만 1로 


(3) 배타적 논리합 연산자(XOR- ^) : 좌우의 식을 2진수로 하는 배타적논 

리합을 구한다. 


a=117, b=216일 때 a^b의 결과 

117 => 0000 0000 0111 0101 

216 => 0000 0000 1101 1000 

============================ 

0000 0000 1010 1101 => 173 


두 개의 비트가 동일하면 0, 다르면 1이 된다. 


(4) 비트반전 연산자(NOT- ~) : ~는 부호를 반전하는 연산자로서 비트1은 

0으로, 0은 1로 만든다. (1의 보수 연산자라고도 한다) 


a=117일 때 ~a의 결과 

117 => 0000 0000 0111 0101 

============================ 

1111 1111 1000 1010 => -118 


(5) shift 연산자( <<,>> ) : 좌측 식의 결과를 우측식만큼 비트를 이동 

시킨다. 이 때 밀리는 쪽의 맨 끝 비트는 밀려나오고, 반대쪽이 마 

지막 비트는 0으로 채워진다. 

좌측으로 쉬프트한 경우에는 맨 우측에 무조건 0이 채워지지만 우 

측으로 쉬프트한 경우에는 부호가 없는 정수이거나 맨 좌측의 비트 

가 0(양수)이면 0을 채우고, 부호가 있는 정수이고 맨 좌측의 비트 

가 1(음수)일 때는 1을 채운다. 즉, 쓸데 없이 부호를 바꾸지 않는 

다고 볼 수 있다. 


a=117일 때 a << 2 의 결과 

117 => 0000 0000 0111 0101 

============================ 

00 0000 0001 1101 0100 <= 00 


또한 비트 연산자는 등호와 결합해서 치환 연산자를 이룰 수 있다. 

a=a >> b; 는 a >>= b, a=a & b; 는 a &= b와 같다. 


초보자들은 비트 연산자들의 쓰임에 대해 의구심을 가질지 모르나, 고급 

프로그램에서는 아주 요긴하게 쓰이고 있다. 비트 연산이 C의 저급언어의 

특성을 대변하는 특징 주의 하나라고 볼 수 있다. 특히, 한글 출력에 있어 

서의 비트 연산의 쓰임은 필수적이다. 여러분들이 고급 과정으로 올라가면 

이러한 이야기들이 모두 이해가 갈 것이다. 지금 우리는 이러한 비트 연산 

의 기능에 대해서만 알고 있자. 



6. 순차 연산자( , ) - comma 연산자 


여러개의 식을 한 줄에 나열하는 기능을 가진 순차 연산자는 우선 순위가 

가장 낮으며, 좌결합성을 가지며, 컴머 우측에 있는 식을 평가하여 얻은 

값을 결과로 한다. (리스트2의 5행에 순차 연산자가 쓰였다) 


x=(y=1, y++); 는 x=2, y=2 가 된다. 

x=(y=1, ++y); 는 y=1, x=++y이므로 x=2, y=2가 된다. 

x=(y=1, y+1); 은 y=1, x=y+1이므로 x=2, y=1이 된다. 

x=(y=1, z=2); 는 y=1, z=2, x=2가 된다. 

x=(y=3, y+2); 는 y=3, x=5가 된다. 


<예제3> 아래 프로그램에서 x와 y의 값은 어떤 결과가 나오겠는가? 


<리스트3> #include 

main() 

int i,j; 

i=(j=15, j+4); 

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

결과는 직접 실행해 보시기 바란다. 



7. 조건 연산자 ( ? : ) - 식1 ? 식2 : 식3 


if - else문을 연산자로 구현한 것으로서 3항 연산자이다. 

식1을 평가하여 참(1)이면 식2의 값을, 거짓(0)이면 식3의 값을 가진다. 

우결합성을 가지므로 여러번 기술하였을 때는 맨 우측부터 평가된다. 


<예제4> if (a > b) c=a; 

else c=b; 를 조건연산자로 구현하여라. 또, a=1,b=2일 때 조 

건 연산을 행한뒤 a, b, c의 값은 어떻게 되겠는구아? 


<리스트4> 위의 조건식을 c=(a > b)? a: b;로 표현할 수 있다. 

#include 

main() 

int a,b,c; 

a=1; 

b=2; 

c=(a > b) ? a:b; 

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


x=(y > z) ? 1 : (y < z) ? 2 : 3;은 다음과 같다. 


i) y > z 이면 x=1이 된다. 

ii) y > z가 아니면 y < z를 평가한다. y < z이면 x=2가 된다. 

iii) i) ii) 둘다 아니면 x=3 이 된다. 

위의 식을 if - else문으로 고치면 아래와 같다. 


if (y > z) x=1; 

else if (y < z) x=2; 

else x=3; 




8. 간접 연산자 ( * ) 


포인터라고도 하며, 식이 가르키는 번지에 저장된 내용을 표시하는 연산 

자이다. 단항 연산자로서 곱하기(*)하고는 질적으로 틀린다. 

모든 자료는 컴퓨터의 메모리에 저장되며, 메모리는 바이트 단위로 번지 

가 붙어있다. 포인터는 그 변수의 값이 가리키는 번지의 자료를 지정하는 

것이라고 우선 알고 있자. 



9. 번지 연산자 ( & ) - & 변수 


번지 연산자는 바로 다음에 오는 변수의 번지 자체를 구해 주는 연산자이 

다. 수치 자료를 입력받을 경우(scanf)에 쓴 기억이 날 것이다. &는 상수 

에는 쓸 수 없으며, 이 연산자가 붙은 경우에는 새로운 값을 대입할 수 없 

다. 즉 &3 이나 &x=1234 등은 에러이다. 위 <리스트3>의 맨 마지막 행에 

printf("%x ,&i)를 삽입시켜 보아라. 변수 i의 주소값이 16진수로 표시 될 

것이다. 



10. cast 연산자 - (형) 연산자 


형변환 연산자는 원하는 데이터의 형을 괄호로 묶어 연산자 앞에 지정해 

둠으로써 데이터 형을 변환시키는 역할을 한다. 


<예제5> cast연산의 보기 - 실행결과를 예측해 보라. 


<리스트5> 1: #include 

2: main() 

3: { 

4: char ch; 

5: int i; 

6: float fl; 

7: 

8: fl=i=ch='A' 

9: printf("ch=%c,i=%d,fl=%2.2f\n",ch,i,fl); 

10: 

11: ch++; 

12: i=fl + 2 * ch; 

13: fl=2.0 * ch + 1; 

14: printf("ch=%c, i=%d, fl=%2.2f\n",ch,i,fl); 

15: 

16: ch=2.0e30; 

17: printf("Now ch=%c\n",ch); 

18: } 


<설명> 8,9행: 문자 'A'는 변수 ch에 문자로 지정됨. 정수변수 i는 'A' 

를 정수로 변환한 65를 받음. fl은 65를 65.00으로 변환한 값을 받음. 

11,14행: 문자변수 'A'를 정수 65로 변환하고 여기에 1을 더함. 그리고 

정수 66을 문자 B로 변환하여 ch에 저장함 

12,14행: ch의 값을 정수로 변환한 다음 2와 곱함. 결과(132)를 실수로 

변환해 fl과 더함. 결과 197.00은 정수로 바뀌어 i에 저장됨. 

13,14행: ch의 값 'B'를 실수로 변환해 2.0과 곱함. i의 값(197)을 실수 

로 변환해 더한다. 결과(329.00)은 fl에 저장됨. 

16,17행: ch를 매우 큰 수로 치환하므로 예기치 않은 결과가 일어남. 


<예제6> cast 연산자의 사용 예 


<리스트6> main() 

int i,j; 

i=372; 

j=5; 

printf("%5.3f\n", (float)i / (float)j ); 



11. sizeof 연산자 



sizeof는 바로 다음에 나오는 변수나 자료형이 차지하는 메모리의 바이트 

수를 구해주는 연산자이다. 


char c; 

sizeof(c); 1의 값을 가진다. 

sizeof(int); 2의 값을 가진다. 



이렇게 해서 C의 모든 연산자에 대해 간략하게 나마 알아 보았다. 초보자 

들에겐 부담스러운 양이겠지만- 또, 쓸데없는 연산자가 왜이리 많냐고 -, 

C언어는 고급언어와 저급언어의 특징을 골고루 갖추고 있고, 저급언어의 

특성을 십분 활용하려면 이러한 연산자들을 잘 사용할 줄 알아야 한다. 



12. 연산자를 사용한 예제들 



몇 가지 예제를 통하여 연산자들의 실제 사용법에 관하여 살펴보자. 


<예제7> 임의의 정수를 입력받아 그수를 2진수로 출력하는 프로그램작성. 

(C는 이진수를 지원하지 않는다) 


<리스트7> #include 

main() 

int i,n,k; 

printf("정수입력: "); 

scanf("%d", &n); 

for ( i=15; i >= 0; i--) { 

k=(n >> i) & 0x1; /* 쉬프트연산, 비트연산 */ 

printf("%1d",k); 


<참고> C에서 8진수의 표기는 수치앞에 0을 붙여 표시하고, 16진수는 0x를 

붙여 표시한다. 이러한 수들은 항상 부호없는 정수로 취급한다. printf 

()함수의 포맷코드는 각각 %o와 %x이다. 


C에서 2진수를 출력하려면 최상위 비트부터 그 비트 패턴(1/0)을 출력시 

키는 방법을 사용한다. 

비트 패턴을 출력하는 방법은 우선 출력하려는 비트를 최하위 비트가 되 

도록 우로 쉬프트시킨 다음 이 값을 0000000000000001과 &(AND) 연산을 시 

켜, 최하위 비트를 제외한 다른 모든 비트들은 0의로 ゼ한다. 만약, 자리 


수가 16개이면 이런 작업을 16번 반복하여 얻어진 값을 하나씩 출력한다. 


<그림1> 상위에서 13번째 비트패턴을 조사하는 과정 

0 1 0 1 1 0 1 0 1 0 0 0 1 1 1 0 원래의 수 


0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 우로 13회 쉬프트 


0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1과 & 연산 

==================================== 

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 13번째 패턴이 구해졌다! 


<예제8> 123456789 와 같이 출력하는 프로그램을 작성하여라. 

*0000000* 1 (힌트: 가로+세로의 값이 10일 때와 

0*00000*0 2 가로와 세로의 값이 같을 때 

00*000*00 3 *을 출력하는 프로그램) 

000*0*000 4 

0000*0000 5 

000*0*000 6 

00*000*00 7 

0*00000*0 8 

*0000000* 9 


<리스트8> main() 

int i,j; 

for (i=1;i <= 9;i++) 

for (j=1;j <= 9; j++) 

i == j || i + j == 10 ? printf("*") : printf("0"); 

printf("\n"); 


위의 프로그램에서 단문과 복문 혼동으로 인한 에러가 있다. 그것을 직 

접 찾아보기 바란다. 


이번 장에서는 C의 연산자에 대해 알아 보았다. 되도록 이해하기 쉽도록 

설명하려 노력을 했다. 7번 정도 읽으면 외우지 못 할 것도 없다. 왠만한 C 

입문서에 버금가는 분량의 내용이므로 '학'실히 예제를 수행해 보면 모두 

이해할 수 있을 것이다. 



반응형

'IT > Programming' 카테고리의 다른 글

제 6장 - 배열과 포인터  (0) 2016.03.22
제 5장 - 함수 / 기억부류  (0) 2016.03.22
프로그래밍 기초 3  (0) 2016.03.22
제 2장 - C의 입출력 함수 및 수의 표현  (0) 2016.03.22
제 1장 - C의 구조와 변수  (0) 2016.03.22
Comments