관리 메뉴

Kim's Programming

C언어 - 연산자(2/2) 본문

Programming/C

C언어 - 연산자(2/2)

Programmer. 2015. 7. 22. 15:07

7. 비트 연산자

 

비트 연산자는 생김새는 논리 연산자와 비슷하지만 비트에 대한 연산을 하는 연산자입니다. 비트(bit)란 기억 장치의 최소의 단위로 0 또는 1로 저장되며 비트 8개를 모아 1바이트가 됩니다.  우선 비트 연산자의 종류에 대해서 알아 보겠습니다. 현재는 잘 사용 되지는 않으며 중요한 시스템 소프트웨어나 게임 또는 임베디드 등에 쓰입니다.

 

 

비트 연산자 

 의미

 ~

 비트를 반전 시킵니다.

 &

 대응되는 비트가 모두 1일때 1이 됨

 |

 대응되는 비트가 모두 0일때 0이됨

 ^

 두 개의 비트가 달라야 1임

 <<

 지정한 수 만큼 왼쪽으로 비트들을 이동

 >>

 지정된 수 만큼 오른쪽으로 비트들을 이동

 

~만 단항 연산자이며 나머지는 모두 두 개를 피연사자로 취하는 이항 연산자 형태입니다. 비트 연산은 정수 수준에서만 의미가 있으며 이때문에 피연산자는 모두 정수형이거나 정수로 변환 될 수 있는 타입이어야 합니다. 실수, 포인터는 비트연산자를 사용할 수 없습니다. 우선 비트 진리표부터 보고 가겠습니다.

 

 

 bit1

 bit2

 bit1 & bi2 

 bit1 | bit2

bit1 ^ bit2 

 ~bit1

 0

 0

 0

 0

 0

 1

 0

 1

 0

 1

 1

 1

 1

 0

 0

 1

 1

 0

 1

 1

 1

 1

 0

 0

 

비트연산자는 난잡하지만 소스를 통해서 알아보도록 하겠습니다. 비트계산을 구현한 소스입니다.

 

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
 
void bit(int num)
{
    for (int i=7; i >= 0--i)
    {
        printf("%d", (num >> i) & 1);
    }
}
 
void main()
{
    int num_1 = 0,num_2 = ;
    
    printf("첫번째 정수를 입력하세요 : ");//0~255
    scanf("%d"&num_1);
    printf("두번째 정수를 입력하세요 : ");//0~255
    scanf("%d"&num_2);
 
    printf("\n\n");
    printf("입력 받은 두 숫자의 2진수 값 입니다.\n");
    printf("첫 번째 정수 :");
    bit(num_1);
    printf("\n두 번째 정수 :");
    bit(num_2);
 
    printf("\n\n첫 번째 정수의 ~ 연산 : ");
    bit(~num_1);
    printf("\n두 번째 정수의 ~ 연산 : ");
    bit(~num_2); 
 
    printf("\n\n두 비트의 &연산\n");
    bit(num_1); printf(" & "); bit(num_2); printf(" = "); bit(num_1 & num_2); printf(" = "); printf("0x%X", num_1&num_2);
    
    printf("\n\n두 비트의 |연산\n");
    bit(num_1); printf(" | "); bit(num_2); printf(" = "); bit(num_1 | num_2); printf(" = "); printf("0x%X", num_1|num_2);
 
    printf("\n\n두 비트의 ^연산\n");
    bit(num_1); printf(" ^ "); bit(num_2); printf(" = "); bit(num_1^num_2); printf(" = "); printf("0x%X", num_1^num_2);
 
    printf("\n\n첫번째 정수의 >>2 연산\n");
    bit(num_1); printf(" >> 2"); printf(" = "); bit(num_1 >> 2); printf(" = "); printf("0x%X", num_1>>2);
 
    printf("\n\n첫번째 정수의 <<2 연산\n");
    bit(num_1); printf(" << 2"); printf(" = "); bit(num_1 << 2); printf(" = "); printf("0x%X", num_1<<2);
 
    printf("\n");
}
 
cs

 

다음 소스는 2개의 정수를 입력받아서 2진수료 표기한 뒤 각 비트 연산자들에 따른 계산을 나타내는 소스입니다. 그럼 우선 입력후의 임의 입력후의 결과 계산을 먼져 보고 각 계산에 대한 설명을 보겠습니다.  

 

 

다음과 같이 출력이 되는 소스입니다. 순서대로 차례 차례 알아보도록 하겠습니다.

 

~ 연산자

 

~ 연산자는 0인 비트는 1로 1인 비트는 0으로 바꿔주는 연산자입니다.  위에서 확인 할 수 있습니다. 원래 11101010 값을 가졌던 첫번 째 정수에 ~연산을 적용 시키니 00010101값을 가지게 되었습니다. 이 두수는 1의 보수관계에 있으며 두개를 더했을 경우 0xFF(11111111)이 됩니다. 만약에 2진수를 이용하여 비교를 하면 간단 하지만 이것이 정수형이 었다면 구별이 힘들게 됩니다. 또 2진수로 나타 내기엔 너무 길게 됩니다. 그래서 보통 16진수로 나타내어 표현을 하게됩니다. 정수에 비해서는 알아보기 쉽고 2진수에 비하면 충분히 짧기 때문이죠

 

& 연산자

 

~ 연산자는 위에서 볼 수 있 듯 0과 &연산에 들어가게 되면 무조건 0이 됩니다. 1과 1이 &연산되는 경우만 1이 되게 됩니다. 음 8번째 부터 5번째 까지 4개의 비트가 강제로 0이 되어 버리는데 이런식으로 특정 비트를 강제로 0으로 만드는 비트를 마스크 오프(mask off)라고 부릅니다. 그럼 이와 반대로 하는 | 연산자도 알아보겠습니다..

 

| 연산자

 

|연산자는 ~연산자와 반대로 1과 |연산을 하는 모든 비트를 1로 만들어 버립니다. 0과 0이 |연산 되는 경우에만 0을 출력 하게 됩니다. 이중에 이렇게 특정 비트를 강제로 1로 만드는 연산을 마스크 온(mask on)이라고 부릅니다.

 

|연산자와 &연산자는 일부 비트만 제한적으로 읽거나 변경할 때 흔하게 이용하게 됩니다. 기억 공간을 절약하기 위해 하나의 정수값을 비트별로 잘라 여러 가지 값을 같이 기억시키는 방법이 이용 되는데 한글 조합형 코드는 16비트 길이를 가지며 다음과 같이 구성되있습니다.

 

 1(한글 비트)

 초성

중성 

종성 

 

최상위 비트(제일 앞 비트)는 항상 1인데 이는 한글임을 의미합니다. 영문 알파벳은 모두 128보다 작기 때문에 이 비트가 0으로 되어 있어 한글과 구분이 됩니다. 16비트의 정수 값을 5비트씩 잘라서 초성, 중성, 종성 코드를 기억시킵니다. 한글의 자음(ㄱ,ㄹ,ㄷ,..)은 32개가 채 되지 않기 때문에 5비트 내로 표현이 가능합니다. 그렇게 할당된 초성,중성,종성이 모이면 한글이 이루어 지게 됩니다. 이렇게 초성, 중성, 종성을 따로 기억 시키게 되면 정수를 이용하여 기억하는 방법에 비해서 훨씬 절약이 되게 됩니다. (만약 초성 하나에 정수형을 취하게 되면 4바이트를 취하게 되어 각각 4바이트씩 취하게 되면 12비트를 이용해야 한글 한글자를 구성하게 되지만, 비트 단위로 나누게 되면 한글자 전체에 2바이트로 형성이 되게 됩니다. 이렇게 비교하면 엄청 이득이죠?)  또 특정 비트부분과 연산자 &나 |이용하여 어디 부분을 1또는 0으로 바꿈으로써 쉽게 한글을 바꿀 수 있습니다.

 

^ 연산자

 

 

^연산자는 배타적 논리 합이라고 부르며 ~연산자와 마찬가지로 비트를 반전 시키는 기능을 하는 연산자입니다. 하지만 ~연산자와 다른것은 ^연산자는 지정한 위치의 비트만 바꾸게 됩니다. ^연산자는 서로 다를때 1이 되고 같으면 0이 되는 연산이기 때문에 1과 ^되는 비트는 반전되고 0과 ^되는 비트는 원래값을 유지한다는 것을 위에서 확인을 할 수 있습니다. 이렇게 특정한 부분만 비트를 반전시키고 싶을때 그 특정 비트들만 1로 된 비트와 원래 같을 ^연산을 시키면 원하는 부분만 반전시킬 수 있습니다.

 

쉬프트 연산자

 

쉬프트 연산자는 말그대로 비트를 이동시키는 것입니다. (위의 결과값 중에서 왼쪽으로 이동은 자리수가 부족하여 1자리가 생략된 상태입니다) 비트를 해당 위치로 옮기고 빈 자리엔 0을 채워주게 됩니다.

 

 

 

7. 삼항 조건 연산자

 

삼항 조건 연산자는 다른언어에는 없는 C언어의 독특한 연산자이며 피연산자를 3개를 갖게 되는 연산자입니다. 삼항 조건 연산자의 형태는 다음과 같습니다.

 

(조건문) ? 값1 : 값2

 

다음 예시를 통해서 어떻게 작용 하는지 알아 보겠습니다.

 

1
2
3
4
5
6
7
8
9
#include<stdio.h>
 
void main()
{
    int i = 10, j = 8, k = 0;
 
    k = (i < j) ? i : j;
    printf("k값은? = %d\n", k);
}
cs

 

 

k라는 변수 현재 삼항 조건의 값을 받게 되어 있는데 무슨 뜻인지 알아볼까요? k에 조건 괄호 안에 들어간 조건에 따라 판단을 해서 참이라면 왼쪽의 i 값을 출력하고 거짓이라면 j를 출력하는 함 수 입니다. 다음과 같은 변수 값을 가진 경우 결과는 다음과 같이 됩니다.

i = 10이고 j는 8이므로 비교를 했을때 i가 더 큰 값이기 때문에 거짓입니다. 그렇기 때문에 2번째 값인 8이 k의 값이 되며 출력이 되었습니다.

 

 

8. 쉼표 연산자

 

쉼표 연산자는 말 그대로 (,)쉼포를 이용합니다. 쉼표연산자의 의미는 양쪽에 두개의 표현식을 취하는 형태를 가지며 좌변을 먼저 평가한고 우변을 평가한후 우변의 연산 결과를 리턴하는 연산자 입니다. 이 연산자는 연산의 기능보단 연산식 통합역할에 더 가까운 기능을 가지고 있습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
#include<stdio.h>
 
void main()
{
    int i = 3;
    int j = i+3;
    printf("i = %d, j = %d\n\n", i, j);
 
    int a, b;
    a = (b = 5, b + 2);
    printf("a = %d, b = %d\n", a, b);
}
cs

 

위쪽 부분에서 보면 i에 3을 대입하고 그 i값을 이용해서 j에 대입후 출력을 합니다. 하지만 10번째 쭐을 어떻게 작용할까요? 우선 저것의 원리는 좌측 부터 우측으로 입니다. 좌측 즉, b에 5를 대입합니다. 그다음 우측 b+2를 연산을 하여 결과를 리턴합니다. 리턴한 7은 a가 받습니다. 결과를 보면 좀 이해가 빨라 질 것입니다.

 

9. sizeof 연산자

 

sizeof 연산자는 기호로 되어있지 않고 단어로 된 연산자입니다. sizeof 연산자는 주어진 타입 또는 변수의 크기를 계산하는 연산자 입니다. 형태는 타음과 같습니다.

 

sizeof(타입 또는 변수)

 

sizeof에 대해서는 변수설명 파트에서 예시를 들었으므로 생략하도록 하겠습니다.

 

10. 캐스트 연산자

 

잠시 특정 변수의 형태나 특정 수식의 결과 리턴 값의 형태를 바꾸기 위해서 쓰게 되는 연산자입니다. 변환이 필요한 특정 변수를 잠깐 변경 시켜줍니다. 다음 소스를 보면서 설명 하겠습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
#include<stdio.h>
 
void main()
{
    int i = 3;
    int j = 5;
    double k = 0;
    k = j / i;
    printf("k = %f\n", k);
    k = (double)j / i;
    printf("k = %f\n", k);
}
cs

 

다음 소스의 경우를 보겠습니다. 8번째줄과 9번째 줄을 보겠습니다. 나누기를 해서 fouble인 k에 대입을 하지만 i와 j 모두 정수 값이기 때문에 결과값을 1.xxx가 나와도 정수형으로 인식하여 1.000000가 나오게됩니다. 하지만 10번째 줄에서는 캐스트 연산자를 사용했습니다.ji앞에 (double)을 붙여 줌으로써 잠깐 속여주는 것입니다. 그렇게 j는 double 형태를 띄게 되어 계산이 되고 결과값은 double형태로 리턴이 되게 됩니다. 그렇기 때문에 다음과 같은 결과가 나오게됩니다.

 

 

#. 연산 순위

 

모든 연산자에는 우선 순위가 있습니다. 마치 +가 *보다 나중에 계산된다는 기본적인 사칙연산들을 기억해보시면 그럴 것 입니다. 표를 통해서 순서를 알아 보겠습니다.

 

 순위 

 연산자

 결합순서

 1

 ( ) [ ] -> .

 왼쪽 우선

 2

 ! ~ ++ -- + -(부호) *(포인터) & sizeof 캐스트

 오른쪽 우선

 3

 *(곱셈) / %

 왼쪽 우선

 4

 + -(덧셈, 뺄셈)

 왼쪽 우선

 5

 << >>

 왼쪽 우선

 6

 < <= > >=

 왼쪽 우선

 7

 == !=

 왼쪽 우선

 8

 &

 왼쪽 우선

 9

 ^

 왼쪽 우선

 10

 |

 왼쪽 우선

 11

 &&

 왼쪽 우선

 12

 ||

 왼쪽 우선

 13

 ? :

 오른쪽 우선

 14

 = 복합대입

 오른쪽 우선

 15

 ,

 왼쪽 우선

 

연산 순위는 1순위 부터 15순위가 매겨져있으면서 순위가 빠른 연산자가 먼져 실행이 됩니다.