본문 바로가기

연산자

1. 연산자

 

3 + 5 = 8

 

위 식의 +와 같이 값의 연산을 위해 사용되는 기호를 연산자(operator),

3, 5와 같이 연산되는 값을 피연산자(operand)라 한다.

 

초등학교 수학에서 배우는 +, -, ×, ÷ 등이 연산자의 예다.

 

연산자는 기능에 따라 다음과 같이 나누겠다.

 

산술 연산자, 비트 연산자, 논리 연산자, 대입 연산자, 관계 연산자, 기타 연산자

 

참고로 피연산자의 수를 기준으로 해서 다음과 같이 나눌 수도 있다.  

 

단항 연산자(unary operator), 이항 연산자(binary operator), 삼항 연산자(ternary operator)

 

 

 

 

 

2. 대입 연산자와 산술 연산자

 

산술 연산은 사칙 연산과 같이 수치에 대한 연산을 말한다.

 

연산자 

기능 

결합 방향

 예시

=

우변의 값을 좌변의 변수에 대입한다. 

 ←

num = 5;

+

 왼쪽의 피연산자와 오른쪽의 피연산자를 더한 값을 반환한다.

num = 5 + 3;

-

 왼쪽의 피연산자에서 오른쪽의 피연산자를 뺀 값을 반환한다.

num = 5 - 3;

*

 왼쪽의 피연산자와 오른쪽의 피연산자를 곱한 값을 반환한다.

num = 5 * 3;

/

 왼쪽의 피연산자를 오른쪽의 피연산자로 나눈 값을 반환한다.

num = 5 / 3;

%

 왼쪽의 피연산자를 오른쪽의 피연산자로 나눈 나머지를 반환한다.

 num = 5 % 3;

 

이들은 모두 이항 연산자에 속한다.

결합 방향이 무엇인지는 아직 몰라도 좋다. 조금 뒤에 설명하겠다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
 
int main()
{
    int a = 5, b = 3;
 
    printf("%d + %d = %d\n", a, b, a + b);
    printf("%d - %d = %d\n", a, b, a - b);
    printf("%d * %d = %d\n", a, b, a * b);
    printf("%d / %d = %d\n", a, b, a / b);
    printf("%d / %d의 나머지 = %d\n", a, b, a%b);
 
    return 0;
}
cs

 

 

 

5 / 3 = 1에 주목하자. 

실제로는 5 / 3 = 1.666666666이다. 

하지만 여기서는 정수끼리의 연산이므로 소수부의 손실에 의해  1 이 저장된다.

따라서 정수 / 정수의 결과는 정수 ÷ 정수의 이라고 할 수 있다.

 

 

 

3. 컴퓨터의 데이터 표현 방식

 

비트 연산자에 앞서 다시 한번 비트와 바이트에 대해 알아보자.

비트는 2진수 하나를 저장할 수 있는 메모리의 크기,

즉 컴퓨터가 데이터를 표현하는 최소 단위고 바이트는 비트 8개를 묶은 것이라 했다.

 

따라서 컴퓨터에서 실제로 데이터가 어떻게 저장되는지, 

또 비트 연산은 어떻게 일어나는지 파악하기 위해서는 2진법에 대한 이해가 필요하다.

 

컴퓨터는 2진수를 기반으로 데이터를 표현하고 연산한다.

즉 두 개의 *기호로 데이터를 표현한다는 것이다.

 

사람은 10진수를 기반으로 데이터를 표현하고 연산한다.

즉 열 개의 기호로 데이터를 표현한다는 것이다.

 

*기호: 숫자도 일종의 기호다. 이라는 수를 표현하는 기호는  이라는 숫자인 것이다.

 

그러니까 같은 수를 표현하는 방법이 다르다는 이야기다.

예를 들어 125라는 숫자를 나타낼 때, 사람은 15로나타내지만 컴퓨터는 1111101로 나타낸다.

0~9로 나타내는 사람과 달리 컴퓨터는 0과 1로만 나타내기 때문이다.

 

하지만 기본적인 원리는 같다.

 

이해를 돕기 위해 우선 10진수로 생각해보겠다.

 

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10.

 

9 다음은 10이다. 자릿수가 하나 늘었다. 이것은 십진수기 때문이다.

당연한 이야기를 한다 싶겠지만, 이는 한 자리에서 표현할 수 있는 수가 9개라는 것을 의미한다.

 

이번엔 173을 보자.

단, 이것을 이 아니라 으로 보자.

 

숫자 173은 다음을 의미한다.

 

 

감이 오는가?.

 

n진법의 n은 자릿수를 나타내는 것이다.

그러니까 n진법이라는 것은 기본적으로 숫자를 다음과 같이 표현하는 것이다.

 

 

 

따라서 10진수로 십삼을 나타내면 13이지만, 이진수로 나타내면 다음 식에 의해 1101이 된다.

 

 

 

 num = 13; 과 같이 변수에 10진수로 저장해도, 

실제 컴퓨터 메모리에는  1101 로 저장되는 것이다.

 

더불어 8진수와 16진수에 대해서도 알아보자.

특히 16진수의 경우 2진수의 자릿수가 너무 길어지면 16진수로 표현하는 경우가 많이 때문에

알아두는 것이 좋다. 

8진수는 0~7의 숫자를 사용한다는 것만 알아도 충분하므로 따로 언급하지 않겠다.

 

16진수도 10진수와 크게 다를 게 없다. 다른 것은 자릿수가 16의 거듭제곱이라는 것 뿐이다.

다만 0~9의 숫자로만 나타낼 수 있는 2진수, 8진수, 10진수와 달리 5개의 기호가 더 필요하다.

 

16진수는 0~9의 숫자와 A~F(소문자도 가능)의 알파벳으로 나타낸다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2진수

0

1

10

11

100

101

110

111

1000

1001

1010

1011

1000

1101

1110

1111

10진수

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16진수

0

1

2

3

4

5

6

7

8

9

A

B

C

D

F

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

참고로 네 자리의 2진수 한 자리의 16진수로 나타낼 수 있다.

그래서 자릿수가 긴 2진수를 16진수로 표현하는 것이다.

 

그리고 8진수는 숫자 앞에  0 을 붙여서  043 과 같이 나타내고 

16진수는 앞에  0x 를 붙여서  0xA8 과 같이 나타낸다.

2진수를 나타내는 기호는 없다.

 

몇 가지 예시를 들겠다.

 

Q. 10진수 78을 2진수로 나타내면?

 

A:

 

Q. 10진수 189를 16진수로 나타내면?

 

A:

 

Q. 10진수 67을 2진수, 8진수, 16진수로 나타내면?

 

A:

 

 

2진수는 1바이트, 즉 8비트 단위로 나타내는 것이 보편적이다. 

 

 

 

4. 음수를 나타내는 방법

 

3.1 변수와 자료형에서 다음과 같은 이야기를 했다.

 

최상위 비트가 0이면 양수, 1이면 음수로 취급한다.

 

이 문장만 보면 양의 정수에서 최상위 비트만 1로 바꾸면 절댓값이 같은 

음의 정수가 될 것 같지만, 실제로는 그렇지 않다.

 

예를 들어, 숫자 -5을 1바이트짜리 변수에 저장하려고 한다고 해보자.

 

5는 2진수로 00000101이다. 

최상위 비트만 1로 바꾸어 10000101로 저장한다면, 이 값이 -5이라고 확신할 수 있을까?

 

만약 10000101이 정말 -5라면, 00000101과 더했을 때 0이 되어야 할 것이다.

정말 이랬다면 편했겠지만 다음 식을 보면 알 수 있듯이 실제로는 그렇지 않다.

 

    10000101

+  00000101

    10001010 ≠ 0

 

최상위 비트만 1로 바꾸는 방법이 틀렸다는 것은 너무나도 간단하게 증명되어버렸다.

그렇다면 음의 정수는 어떻게 나타내야 할까?

 

음의 정수를 나타내기 위해서는 2의 보수법이라는 것이 필요하다.

2의 보수법은 다음의 과정을 말한다. 5를 예로 들어 설명하겠다.

 

각 비트에서 0은 1로, 1은 0으로 바꾼다.

 

Ex) 00000101 -> 11111010

 

 

② 1을 더한다.

 

Ex) 11111010 -> 11111011

 

위와 같은 과정에 의해 -5는 11111011로 표현된다.

 

이제 정말 -5가 맞는지 확인해보자.

 

    11111011

+  00000101

   100000000 = 0

 

1바이트의 변수에 저장하므로 맨 앞의 1은 버려진다.

따라서 11111011은 -5라고 할 수 있다.

 

 

 

5. 관계 연산자 

 

관계 연산자는 대소 관계나 동등 관계를 판단할 때 쓰인다.

 

산자 

예시

기능 

결합 방향 

<

a < b

 a 가 b보다 작으면 참, 그렇지 않으면 거짓을 반환한다.

>

a > b

 a 가 b보다 면 , 그렇지 않으면 거짓을 반환한다.

==

a == b

 a와 b가 서로 같으면 , 그렇지 않으면 거짓을 반환한다.

!=

a != b

 a와 b가 서로 다르면 , 그렇지 않으면 0을 반환한다.

<=

a <= b

 a가 b보다 작거나 같으면 , 그렇지 않으면 거짓을 반환한다.

>=

a >= b

 a가 b보다 작거나 크면 참, 그렇지 않으면 거짓을 반환한다.

 

C언어에서 참 거짓은 각각 1과 0으로 표현한다.

그러나 조건을 만족하면 참을, 만족하지 않으면 거짓을 반환한다는 말이 더 명확하기 때문에,

1과 0이라는 표현은 잘 사용하지 않는다.

 

참고로 0이 아닌 모든 수가 참을 나타낸다.

그러니까 1은 참의 대표값이지만 유일한 값은 아니다.

하지만 0은 거짓의 대표값이자 유일한 값이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
 
int main()
{
    int a = 5, b = 3;
    int result1;
    int result2;
    int result3;
    
    result1 = (a == b);    
    result2 = (a > b);
    result3 = (a <= b);
 
    printf("result1: %d\n", result1);
    printf("result2: %d\n", result2);
    printf("result3: %d\n", result3);
 
    return 0;
}
cs

 

 

여기서 주목할 점은 10~12행의 괄호다.

a와 b에 대한 관계 연산이 먼저 이루어진 다음 result1~result3에 대입되어야 하므로

관계 연산 부분에 괄호를 쳐준 것이다.

 

이처럼 괄호를 통해 연산의 우선순위를 지정할 수도 있다.

 

 

 

6. 논리 연산자

 

논리 연산은 AND(논리곱), OR(논리합), NOT(논리 부정) 연산을 말한다.

 

연산자

예시

기능

결합 방향

&&

A && B

 A와 B 모두 참일 때만 참을 반환한다.

||

A || B

 A와 B 중 하나라도 참이면 참을 반환한다.

!

!A

 A가 거짓이면 참을 반환한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
 
int main()
{
    int a = 5, b = 3;
    int result1;
    int result2;
    int result3;
    
    result1 = (a <= 5 && b == 3);
    result2 = (a == 3 || b == 3);
    result3 = (!(a == 5));
 
    printf("result1: %d\n", result1);
    printf("result2: %d\n", result2);
    printf("result3: %d\n", result3);
    getchar();
 
    return 0;
}
cs

 

 

 a 는  5 보다 작거나 같고  b 는  3 이므로 두 조건을 모두 만족한다. 

따라서 AND 연산의 결과  result1 은 이다.

 

 a 는  3 이 아니지만  b 는  3 이므로 두 조건 중 한 조건을 만족한다.

따라서 OR 연산의 결과  result2 이다.

 

 a 는  5 이므로 a == 5 에 대한 NOT 연산의 결과  result3 거짓이다.

 

참고로  ! 연산자는 피연산자가 하나이므로 단항 연산자다.

 

 

 

7. 비트 연산자

 

비트 연산은 비트에 대해 이루어지는 연산을 말한다.

 

연산자

 예시

기능

결합 방향

&

 a & b

 비트 단위로 AND 연산한 결과를 반환한다.

|

 a | b

 비트 단위로 OR 연산한 결과를 반환한다.

^

 a ^ b

 비트 단위로 XOR(배타적 논리합) 연산한 결과를 반환한다.

~

 ~a

 모든 비트를 반전시킨 결과(1 → 0, 0 → 1)를 반환한다.

<<

 a << 3

 비트 열을 왼쪽으로 이동시킨 결과를 반환한다.

>>

 b >> 2

 비트 열을 오른쪽으로 이동시킨 결과를 반환한다.

 

 

XOR 연산은 둘 중 하나만 참일 때, 즉 서로 다를 때만 참인 연산이다. 

 

&, |, ^의 진리표는 각각 다음과 같다.

 

 

 

 

 

피연산자

연산자

피연산자

반환값

 

피연산자

연산자

피연산자

반환값

 

피연산자

연산자

피연산자

반환값

1

&

1

1

 

1

|

1

1

 

1

^

1

0

1

&

0

0

 

1

|

0

1

 

1

^

0

1

0

&

1

0

 

0

|

1

1

 

0

^

1

1

0

&

0

0

 

0

|

0

0

 

0

^

0

0

 

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
 
int main()
{
    int a = 15//00000000 00000000 00000000 00001111
    int b = 20//00000000 00000000 00000000 00010100
    int result1;
    int result2;
    int result3;
 
    result1 = a & b;
    result2 = a | b;
    result3 = a ^ b;
 
    printf("result1: %d\n", result1);
    printf("result2: %d\n", result2);
    printf("result3: %d\n", result3);
 
    return 0;
}
cs

 

 

 

하나씩 살펴보자.

먼저  result1 의 연산은 다음과 같다. 

 

   00000000 00000000 00000000 00001111

 

&  00000000 00000000 00000000 00010100

   00000000 00000000 00000000 00000100 = 4

 

 

 result2 의 연산 다음과 같다. 

 

   00000000 00000000 00000000 00001111

 

|  00000000 00000000 00000000 00010100

   00000000 00000000 00000000 00011111 = 31

 

 result3 의 연산 다음과 같다. 

 

   00000000 00000000 00000000 00001111

 

^  00000000 00000000 00000000 00010100

   00000000 00000000 00000000 00011011 = 23

 

 

 ~ 연산자는 부호 비트까지 반전시키므로 주의해서 사용해야 한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
 
int main()
{
    int a = 6//00000000 00000000 00000000 00000110
    int result;
 
    result = ~a;
 
    printf("result: %d\n", result);
 
    return 0;
}
cs

 

 

여기에 1을 더하면 6에 2의 보수법을 취한 결과, 즉 -6이 된다.

당연한 이야기지만, 6에 2의 보수법을 취하면 -6이 되듯이

-6에 2의 보수법을 취하면 6이 된다.

 

 

 <<, >> 연산자는 시프트(shift) 연산자라 하는데, 비트 열을 왼쪽, 오른쪽으로 미는 연산이다.

따라서 곱셈이나 나눗셈에도 활용할 수 있다.

사실 곱셈과 나눗셈보다 시프트 연산이 효율이 더 좋다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
 
int main()
{
    int a = 6, b = 9;
    int result1, result2;
 
    result1 = a << 2
    result2 = b >> 1;
 
    printf("result1: %d\n", result1);
    printf("result2: %d\n", result2);
 
    return 0;
}
cs

 

 

 

 a 에는  4 가 곱해졌고,  b 는  2 로 나눠진 것에 주목하자.

 

비트는 2진수다. 따라서 왼쪽으로 한 비트만큼 밀면 2가 곱해지고, 

오른쪽으로 두 비트만큼 밀면 옮기면 4로 나눠지는 것이다.

 

10진수로 생각해보자.

13을 다음과 같이 나타냈다고 하자.

 

1

3

 

이제 이 수를 왼쪽으로 한 칸 밀어보자.

1

3

0

 

10배가 되었다.

 

따라서 다음과 같은 결론을 도출할 수 있다.

 

비트 열을 왼쪽으로 한 칸 이동시킬 때마다 정수의 값은 두 배가 되고

비트 열을 오른쪽으로 한 칸 이동시킬 때마다 정수의 값은 2로 나누어진다.

 

 

 

8. 복합 대입 연산자

 

복합 대입 연산자는 대입 연산자와 다른 연산자와 합쳐진 형태의 연산자다. 

 

 연산자

 예시

기능

결합 방향

+=

 a += b;

a = a + b

-=

 a -= b

a = a - b;

*=

 a *= b

a = a * b;

/=

 a /= b

a = a / b;

&=

 a &= b

a = a & b;

|=

 a |= b

a = a | b;

^=

 a ^= b

a = a ^ b;

<<=

 a <<= b

a = a << b;

>>=

 a >>= b

a = a >> b;

다시 말해,  a += b; 와  a = a + b; 는 동일한 연산이다.

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
 
int main()
{
    int a = 3, b = 4, c = 7, d = 9;
 
    a += 3//a = a + 3;
    b <<= 1//b = b << 1;
    c |= d; //c = c | d;
 
    printf("a: %d\n", a);
    printf("b: %d\n", b);
    printf("c: %d\n", c);
 
    return 0;
}
cs

 

 

 

 

 

9. 기타 연산자

 

사실 아주 많은 연산자가 있지만 지금은 몰라도 되는 게 더 많다.

지금 알아둘 것은 증감 연산자, 삼항 연산자다, 콤마 연산자다.

 

연산자 

예시

기능

결합 방향 

--, ++

  a--, a++

 a의 값을 1 증가시키거나 1 감소시킨다.

--, ++

 --a, ++a

 a의 값을 1 증가시키거나 1 감소시킨다.

?

 (a >= 3) ? a : 2 

 주어진 조건이 참이면 : 왼쪽, 아니면 오른쪽의 값을 반환한다.

,

 printf("a: %d", a);

 둘 이상의 피연산자를 구분한다.

 

 

① 증감 연산자

 

증감 연산자증가 연산자감소 연산자로 나뉘고, 

이들은 각각 전위 연산자후위 연산자로 나뉜다.

 

변수에 저장된 값을 1 증가시키거나 감소시키다는 점은 같지만, 

세부적인 작동 순서가 다르다.

 

연산자 

기능 

결합 방향 

++a 

값을 1 증가시킨 후 속한 문장의 나머지를 실행한다. (선 증가, 후 연산)

a++ 

 속한 문장을 실행한 후 값을 1 증가시킨다. (선 연산, 후 증가)

--a

 값을 1 감소시킨 후 속한 문장의 나머지를 실행한다. (선 감소, 후 연산)

 a--

 속한 문장을 실행한 후 값을 1 감소시킨다. (선 연산, 후 감소)

 

++ 또는 --가 피연산자 왼쪽에 붙으면 전위 증가/감소 연산자,

오른쪽에 붙으면 후위 증가/감소 연산자라 한다.

 

예제를 보면 기능의 의미가 와닿을 것이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
 
int main()
{
    int a = 10, b = 10;
 
    printf("a: %d\n", a);
    printf("a++: %d\n", a++);
    printf("a: %d\n\n", a);
    
    printf("b: %d\n", b);
    printf("++b: %d\n"++b);
    printf("b: %d\n",b);
    
    return 0;
}
cs

 

 

 a 의 경우 후위 증가 연산자이므로  printf()로 출력한 다음  a 의 값을 증가시켰다.

따라서 두 번째  printf()에서는  10 이 출력되었고, 

그 다음  printf()에서는  11 이 출력된 것이다.

 

 b 의 경우 후위 증가 연산자이므로  b 의 값을 증가시킨 다음  printf()로 출력했다.

따라서 두 번째  printf()에서는  10 이 출력되었고, 

그 다음  printf()에서는  11 이 출력된 것이다.

 

 

② 삼항 연산자

 

삼항 연산자는 다음과 같은 형식으로 사용한다.

 

(조건) ? (참일 때의 반환 값) : (거짓일 때의 반환값)

 

주어진 조건이 참일 때는 의 값을, 거짓일 때는 의 오른쪽의 값을 반환한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
 
int main()
{
    int num = 5;
    int result;
 
    result = ((num >= 4) ? (num) : (0));
 
    printf("result: %d\n", result);
    
    return 0;
}
cs

 

 

주어진 조건  num >= 4 가 참을 반환하므로  0 이 아닌  num 이 반환되었다.

 

 

③ 콤마 연산자

 

콤마(comma) 연산자는 다른 연산자들에 비해 특수한 연산자다.

연산의 결과를 반환하는 것이 아니라 구분을 목적으로 사용되는 연산자기 때문이다.

 

콤마 연산자가 사용되는 경우는 주로 다음과 같다.

 

ⅰ. 둘 이상의 변수를 선언하는 경우

ⅱ. 둘 이상의 문장을 한 행에 삽입하는 경우

ⅲ. 둘 이상의 인자를 함수로 전달하는 경우

 

코드로 보면 의미가 와닿을 것이다.

 

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
 
int main()
{
    int a = 1, b = 3;
 
    printf("Hello"), printf("world!\n");
    printf("a: %d, b: %d\n", a, b), a++, b++printf("a: %d, b: %d\n", a, b);
    
    return 0;
}
cs

 

 

 

7행과 8행에는 둘 이상의 함수 호출문과 연산문이 한 문장 안에 들어있다.

이는 콤마 연산자를 사용했기에 가능한 일이다.

 

 

 

10. 연산자 우선순위

 

연산자에는 우선순위결합 방향이 있다. 그러나 둘 다 모두 이미 알고 있으리라 생각한다.

 

우리는 초등학교 때 다음의 규칙을 배운다.

 

사칙연산에서 덧셈과 뺄셈보다 곱셈과 나눗셈을 먼저 해야 한다.

 

이것이 우선순위의 대표적인 예다.

그러니까 우선순위란 여러 개의 연산이 있을 때 어느 연산을 먼저 해야하는가를 말한다.

 

다음의 규칙도 알고 있을 것이다.

 

곱셈과 나눗셈은 왼쪽부터 순서대로 계산한다.

 

 

이것이 결합 방향의 예다.

그러니까 결합 방향이란 하나의 수식에 우선순위가 같은 여러 개의 연산자가 있을 때 어떤 순서로 연산해야 하는가를 의미한다.

 

다음은 연산자 우선순위표다.

 

 

 

연산자의 수는 생각보다 많다.

 

개인적인 생각으로는 우선순위를 외우느니 소괄호로 묶는 게 속편할 것 같다.

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

반복문  (0) 2019.03.03
입력과 출력  (0) 2019.03.02
변수와 자료형  (0) 2019.02.26
C 프로그램의 기본 구조  (1) 2019.02.24
Visual Studio 2017 설치  (0) 2019.02.24