일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 신쥬쿠
- 리눅스
- 스테이크
- youtuber
- paypay
- しまじろう
- one tab buy
- TOY
- 일본
- Shimajirou
- 시마지로
- 동경 모터쇼
- 돈까스
- 명령어
- 시스템관리
- 돼지갈비
- 원탭바이
- 영단어
- 칸칸
- 라면
- Sekai Entertainment
- 米沢、팽이
- 자동차
- 여름
- 코라쿠엔
- 사이타마
- fish
- 전철
- 토익
- 점심
- Today
- Total
IT Japan
제 4장 - 연 산 자 본문
이번 장에서는 프로그램에서 연산을 가능하게 만들어 주는 연산자들에 관
하여 알아 보겠다.
연산자란 대상 자료에 대해 어떤 조작을 하는 부호를 말하며, 이 때 대상
이 되는 자료들을 오퍼랜드(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 |