비트 연산자

정의

  • 정수형(문자형 포함) 값에 대해 비트(bit) 단위로 연산을 수행하는 연산자

종류

연산자 명칭 예제 기능
& 비트 AND
Bitwise AND
a&b 정수 a와 b의 대응되는 두 비트가 모두 1일 때 결과가 1
| 비트 OR
Bitwise OR
a|b ” 중 하나라도 1이면 결과가 1
^ 비트 XOR
Bitwise XOR
a^b ” 가 서로 다를 때만 결과가 1
(Exclusive OR)
~ 비트 NOT
Bitwise NOT
~a 각 비트 1은 0으로, 0은 1로 변환
not true = false / not false = true
<< 왼쪽 시프트
Left Shift
a<<n a 의 각 비트를 n 비트씩 왼쪽으로 이동
이동으로 비워지는 공간은 0으로 채워진다.
>> 오른쪽 시프트
Right Shift
a>>n a 의 각 비트를 n 비트씩 오른쪽으로 이동
이동으로 비워지는 공간은 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
void bit_operator(){
	int a = 0B11001000;
    int b = 0B00111001;

    // & : 비트의 AND 연산
    // 둘 다 1이면 1, 그 외는 모두 0
    printf("a & b  : ");
    print_binary(a&b, 8);

    // | : 비트의 OR 연산
    // 둘 중 하나라도 1이면 1, 둘 다 0이면 0
    printf("a | b  : ");
    print_binary(a|b, 8);

    // ^ : 비트의 XOR 연산
    // 둘이 다르면 1, 같으면 0
    printf("a ^ b  : ");
    print_binary(a^b, 8);

    // ~ : 비트의 NOT 연산
    // 0은 1로, 1은 0으로 반전
    printf("~a     : ");
    print_binary(~a, 8);

    // a << n : 비트의 좌측 이동
    // a의 각 비트를 n자리씩 왼쪽으로 이동
    // 빈 공간은 0으로 채워짐
    printf("a << n : ");
    print_binary(a << 2, 8);

    // a >> n : 비트의 우측 이동
    // a의 각 비트를 n자리씩 오른쪽으로 이동
    // 빈 공간은 0으로 채워짐
    printf("a >> n : ");
    print_binary(a >> 2, 8);
}

void print_binary(int number, int binary_digits){
	// binary 로 출력 (number : 이진수로 변환할 수 / binary_digits : 이진수 자릿수)
	for (int i = (binary_digits-1); i >= 0; i--){
	int result = (number >> i) & 1;
		printf("%d", result);
	}
	printf("\n");
}
1
2
3
4
5
6
a & b  : 00001000
a | b  : 11111001
a ^ b  : 11110001
~a     : 00110111
a << n : 00100000
a >> n : 00110010

비트 연산을 이용한 마스킹

  • 비트 연산을 이용해서, 비트 마스킹을 할 수 있다.
  • 비트 마스킹이란, 피연산 값의 특정 비트를 필터링(통과, 차단)하거나 반전시키는 것을 의미한다.
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
void bit_masking(){
	// 00001111 이라는 마스크 비트를 선언, 정의했다.
	int mask;
    mask = 0B00001111;
    
    // 피연산 비트 값
    int a = 0B10110011;

    // 앞 네 자리를 지우는 마스크
    // 0과 &표시를 하면 상대의 비트값이 뭐든 모두 지워지며
    // 1과 &표시를 하면 상대의 비트값이 그대로 유지된다.
    printf("& mask : ");
    print_binary(a&mask, 8);

    // 특정 비트를 1로 채우는 마스크
    // 0과 |표시를 하면 상대 비트값이 그대로 유지됨
    // 1과 |표시를 하면 연산 결과값이 1이 됨
    printf("| mask : ");
    print_binary(a|mask, 8);

    // 특정 비트값만 반전하기
    // 0과 ^표시를 하면 상대 비트값이 1이면 1, 0이면 0으로 그대로 유지됨
    // 1과 ^표시를 하면 상대 비트값이 1이면 0, 0이면 1로 바뀜 (반전)
    printf("^ mask : ");
    print_binary(a^mask, 8);
}
1
2
3
& mask : 00000011
| mask : 10111111
^ mask : 10111100

2 의 거듭제곱 곱하기 및 나누기

  • 시프트 연산들은 2의 거듭제곱을 곱하거나 나눈 결과와 같다.
  • 왼쪽 시프트 : 2의 거듭제곱 곱하기 연산과 동일
  • 오른쪽 시프트 : 2의 거듭제곱 나누기 연산과 동일
  • 왼쪽 시프트는 오버플로가, 왼쪽 시프트는 음수에서 부호비트 왜곡이 있을 수 있으므로 주의해야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
void square(){
    // << : 좌측이동 : 2^n 제곱을 하는 효과
    int a = 0B00000100; // 4
    int b = a << 3;
    printf("%d\n", b);  // 4 * 2^3 = 4 * 8 = 32
    print_binary(b, 8); // 4 * 2^3 = 4 * 8 = 32

    // >> 우측이동 : 2^n 제곱근을 하는 효과
    int c = a >>2;
    printf("%d\n", c);  // 4 / 2^2 = 4 / 4 = 1
    print_binary(c, 8); // 4 / 2^2 = 4 / 4 = 1
}
1
2
3
4
5
32
00100000

1
00000001

논리연산과 비교하기

  • 논리연산과 비트연산은 그 결과가 다를 수 있다.
  • 이 점을 반드시 주의하기!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void vs_logical_operator(){
    // 논리 연산자와는 계산 결과가 다르다.
    int a = 1;
    int b = 2;

    int result_of_logical = a && b;
    printf("%d\n", result_of_logical);
    print_binary(result_of_logical, 8);

    int result_of_binary = a & b;
    printf("%d\n", result_of_binary);
    printf("a : ");
    print_binary(a, 8);
    printf("b : ");
    print_binary(b, 8);
    print_binary(result_of_binary, 8);
}
1
2
3
4
5
6
7
8
9
# 논리 연산
1
00000001

# 비트 연산
0
a : 00000001
b : 00000010
00000000

먼저, 위 논리 연산에서는 참 값(1)과 참 값(2)의 AND 연산을 통해 결과값이 (1)이 된다.

반면에 비트 연산에서는 0001(1) 과 0010(2) 의 AND 연산을 했기 때문에, 같은 비트가 없어 결과값이 0000(0)이 된다.

Reference

C 프로그래밍 (김형근, 곽덕훈, 정재화 공저)
C 프로그래밍 강의 (방송통신대 - 이병래)

Comments