1. 비트 단위에 관하여

2. 비트 단위의 관리가 왜 필요할까?

3. 시프트 연산자

4. 비트 연산자

1. 비트 단위에 관하여

Untitled

윈도우즈나 리눅스같은 OS는 메모리를 1바이트(Clcik) 단위로 관리 함.

C언어에서는 메모리 할당의 최소 단위는 1바이트(비트단위의 할당은 불가능)

그러나 바이트단위로 메모리를 할당하고 할당된 메모리를 비트단위로 관리 가능.

2. 비트 단위의 관리가 왜 필요할까?

2-1) 메모리 효율이 좋다

1600개의 좌석이 있는 도서관의 좌석 관리 시스템을 만든다고 해보자.

좌석에 사람이 있는 경우를 1, 없는 경우를 0이라 하면

바이트 단위로 관리할 경우 1좌석에 1바이트이므로 1600좌석의 경우 1600바이트를 할당해야한다.

반면에 비트 단위로 관리하면 1좌석에 1비트이므로 8좌석에 1바이트,

1600좌석에 200바이트만 할당하면 된다.

메모리 관리에서 8배의 차이를 볼 수 있으므로 비트단위의 관리가 왜 필요한지 충분한 이유라고 생각된다.

2-2) 속도가 빠르다.

시프트 연산자의 경우 시프트 연산을 담당하는 하드웨어 자체의 성능이 더 좋기때문이다.

그럼 비트단위의 관리는 가능하게 해주는 시프트연산자와 비트연산자에 대해 알아보자.

3. 시프트 연산자

3-1) 시프트 연산이란?

→ 시프트 연산은 2의 거듭 제곱수를 곱셉, 나눗셈 연산을 할때 사용합니다.

→ 시프트 연산을 사용하는 이유는 속도 때문임.

→ 일반적으로 숫자를 곱하거나 나누게 되는 것보다 비트 연산이기 때문에 속도가 더 빠르게됩니다.

→ 이러한 속도는 간단한 프로그램에서는  느껴지지 않습니다.

→ 그러나 엄청난 규모의 프로그램에서는 이런 속도가 도움이 될 것입니다.

3-2) 연산자 기호

Untitled

→ 연산 왼쪽의 숫자를 연산 오른쪽 숫자 비트만큼 좌, 우로 이동하라는 의미입니다.

→ 2진법 숫자 계산이기 때문에 한칸마다 2를 곱하거나 나눕니다.

ex) 4 << 2를 계산해보면

→ 공식으로 해보면 4 * 2^2 = 16

→ 4를 2진법으로 나타내면 0000 0100.

→ 여기서 쉬프트 연산에 의해 왼쪽으로 2비트를 이동.

→ 0000 0100 -> 0001 0000

→ 그러므로 계산해보면 16이 나옵니다.

ex) 16 >> 2를 계산해보면

→ 16 / 2^2 = 4

→ 16을 2진법으로 나타내면 0001 0000

→ 여기서 쉬프트 연산에 의해 오른쪽으로 2비트를 이동

→ 0001 0000 -> 0000 0100

→ 그러므로 계산해보면 4가 나옵니다.

3-3) C언어 코드

#include <stdio.h>
 
int main() {
		// unsigned char는 1바이트로 0~255 표현 가능.
    unsigned char data;
 
    /* << 예제 */
    data = 0x55;// 0101 0101 (85);
    data <<= 1; // 1010 1010 (170)  ---  (왼쪽으로 1비트 이동, 그림 1번)
    data <<= 2; // 1010 1000 (168)  ---  (왼쪽으로 2비트 이동, 그림 2번)
								//									---  underflow 발생!

    data <<= 3; // 0100 0000 (64)   ---  (왼쪽으로 3비트 이동, 그림 3번)
								//									---  underflow 발생!

    /* >> 예제 */
    data = 0xAA;// 1010 1010 (170);
    data >>= 1; // 0101 0101 (85)   ---  (오른쪽으로 1비트 이동, 그림 4번)
    data >>= 2; // 0001 0101 (21)   ---  (오른쪽으로 2비트 이동, 그림 5번) 
								//									---  underflow 발생!
    data >>= 3; // 0000 0010 (2)    ---  (오른쪽으로 3비트 이동, 그림 6번)
								//									---  underflow 발생!
    return 0;
}

Untitled

Untitled

Untitled

Untitled

Untitled

3-3) 시프트 연산자가 어려운 이유에는 3가지 주의사항

첫번째, 오버플로우 혹은 언더플로우

위에서 언급했듯이 자료형의 크기를 넘어가게 될 경우

오버플로우 혹은 언더플로우가  발생할 수 있습니다.

두번째, 자료형의 부호 여부

자료형에 부호 여부에 따라 MSB에 채워질 수 있는 비트가 달라지므로 주의해야 합니다.

세번째, 낮은 우선순위

연산자 우선순위가 낮기때문에 아래와 같은 상황이 발생할 수 있습니다.

4. 비트 연산자

4-1) 비트 연사자란?

비트연산자는 같은 바이트의 크기를 가진 데이터끼리 비트단위로 특정 연산을 하는것입니다.

4-2) 비트 연산의 형태

Untitled

4-3) 비트 연산자의 종류

Untitled

→ 비트연산자별 특성을 이용하면 비트단위로 접근하여 값 변경이 가능합니다.

4-4) C언어 비트 연사자 코드

4-4)-1. &연산

둘 중 하나라도 0이면 결과가 0이 되는 특성이 있습니다.

이 특성을 이용하면 특정 비트를 0으로 변경할 수 있습니다.

#include <stdio.h>

int main() {
    unsigned char data = 0xFF; // 1111 1111
    unsigned char mask = 0x01; // 0000 0001
 
    data & mask; // 0번 비트를 제외하고 전부 0으로 변경
                 // 결과는 0000 0001 
 
    mask = 0x35; // 0011 0101
    data & mask; // 0,2,4,5번 비트를 제외하고 전부 0으로 변경
                 // 결과는 0011 0101
 
    return 0;
}

4-4)-2. |연산

둘 중 하나라도 1이면 결과가 1이 되는 특성이 있습니다.

이 특성을 이용하면 특정 비트를 1로 변경할 수 있습니다.

#include <stdio.h>

int main() {
    unsigned char data = 0x00; // 0000 0000
    unsigned char mask = 0x01; // 0000 0001
 
    data & mask; // 0번 비트만 1으로 변경
                 // 결과는 0000 0001 
 
    mask = 0x35; // 0011 0101
    data & mask; // 0,2,4,5번 비트만 1으로 변경
                 // 결과는 0011 0101
 
    return 0;
}

4-4)-3. ^연산

두 개가 같으면 0, 다르면 1인 특성이 있습니다.

이 특성을 이용하면 초기화, not연산 구현, 암호화 및 복호화가 가능합니다.

/* 간단한 암호화 및 복호화 예제 */

#include <stdio.h>

#define DATA_SIZE 15

int main() {
    char data[DATA_SIZE] = "Hello Tips!!!!";
    char key[DATA_SIZE] = "I love guys :)";
 
    printf("before encryption : %s\\n", data);
 
    for(int i=0;i<15;i++)
        data[i] ^= key[i];
    printf("encryption : %s\\n", data);
 
    for(int i=0;i<15;i++)
        data[i] ^= key[i];
    printf("decryption : %s\\n", data);
    
}

4-4)-4. ~연산

모든 비트가 반전되는 특성이 있습니다.

이 특성을 이용하면 보수를 구할 수 있고, 보수를 이용해 덧셈으로 뺄셈을 구현할 수 있습니다.

먼저 unsigned char의 범위는 0(0000 0000) ~ 255(1111 1111)입니다.

255에서 1을 더하면 1 0000 0000이지만 이는 오버플로우가 생기므로 결국 0이됩니다.

즉, 255 + 1은 0이 되며 이는 255 - 255와 같은 결과입니다.

255 + 2는 1이며 이는 255 - 254와 같은 결과입니다.

255 + 3은 3이며 이는 255 - 253과 같은 결과입니다.

위의 예시들을 통해 덧셈과 뺄셈이 같은 결과가 나옵니다.

이 말은 우리가 뺄셈을 하고싶다면 특정 수를 더해도 된다는 뜻이며, 이 특정 수를 보수라고 합니다.

#include <stdio.h>
 
int main() {
    char A = 0x86; // 1000 0110
    char B = 0x1d; // 0001 1101
    
    /* 1의 보수 */
    ~A; // 0111 1001
    ~B; // 1110 0010
 
    /* 2의 보수 */
    ~A + 1; // 0111 1010
    ~B + 1; // 1110 0011
 
    /* 2의 보수를 이용한 A-B */
    printf("%d\\n",A-B); // -151 출력
    printf("%d\\n",A+(~B+1)); // -151 출력
 
    return 0;
}