ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 5-2장. 비트 단위 연산자와 연산자 마무리
    C++ 기본 문법 알아보기 2019. 1. 8. 12:21

    비트 단위 연산자



    저번 글에서는 자료를 연산자를 통해 가공하고 다뤄보았다.


    이제 연산자를 통해서 비트 수준에서 정보를 다뤄보자.


    우선 가장 기본적인 비트단위 논리 연산자에 대해서 알아보자.


    AND, OR, NOT, XOR을 의미하는 &, |, ~, ^는 동작은 같지만 피연산자가 비트라는 점이 다르다.


    만약 32비트 정수에 논리 연산을 수행한다면 32번의 논리 연산을 수행한다.


    피연산자1의 i번째 비트

    피연산자2의 i번째 비트 

    결과 값의 i번째 비트 

    AND 

    OR 

    XOR 


    XOR는 처음 보는 개념이지만 잘 알아두는 것이 좋다.


    이제 예제를 보고 출력해보자.


    #include <bitset>

    #include <iostream>

    using namespace std;


    int main()

    {

    // 피연산자 선언

    unsigned char a, b;

    a = 178;

    b = 113;


    // 비트 단위 논리 연산 수행

    unsigned char c1, c2, c3, c4;

    c1 = a & b;

    c2 = a | b;

    c3 = a ^ b;

    c4 = ~a;


    // 출력

    cout << "a = " << bitset<8>(a) << "(" << (unsigned int)a << ")\n";

    cout << "b = " << bitset<8>(b) << "(" << (unsigned int)b << ")\n";

    cout << "a & b = " << bitset<8>(c1) << "(" << (unsigned int)c1 << ")\n";

    cout << "a | b = " << bitset<8>(c2) << "(" << (unsigned int)c2 << ")\n";

    cout << "a ^ b = " << bitset<8>(c3) << "(" << (unsigned int)c3 << ")\n";

    cout << "~a = " << bitset<8>(c4) << "(" << (unsigned int)c4 << ")\n";


    return 0;

    }



    출력 결과를 보면 알겠지만 bitset은 2진수로 출력하기 위해 사용한 것을 알 수 있을것이다.


    비트 단위 논리 연산을 응용해보자


    16 비트짜리 RGB 색상이 있다고 했을 때, 보통 앞부터 R은 5비트, G는 6비트, B는 5비트의 값을 갖는다.


    그렇다면 16비트 크기를 갖는 unsigned short 타입 변수에 한 점의 색상이 보관되어 있다고 가정해보자.


    여기서 파란색에 해당하는 부분의 밝기를 구하기 위해서는 어떻게 해야할까??


    AND 연산자를 통한 마스킹을 이용하면 된다.


    예제를 보자.


    #include <bitset>

    #include <iostream>

    using namespace std;


    int main()

    {

    // 한 점의 색상을 보관하는 변수 선언

    unsigned short color = 0x1234;


    // 파란색의 비트만 추출

    // 0x001f는 2진수로 0000 0000 0001 1111

    unsigned short blue = color & 0x001f;


    //출력

    cout << "color = " << bitset<16>(color) << "(" << color << ")\n";

    cout << "blue = " << bitset<16>(blue) << "(" << blue << ")\n";


    return 0;

    }



    파란색 부분만 추출된 것을 확인할 수 있다.



    쉬프트 연산자


    그렇다면 녹색에 해당하는 부분을 추출하고 싶다면 어떻게 해야할까?


    아까와 같이 AND 마스킹으로는 0~255의 값을 얻기는 힘들 것이다.


    이 문제를 해결하기 위해서는 쉬프트 연산자를 사용해야 한다.


    쉬프트 연산자란 비트를 몇 칸씩 옆으로 이동하는 연산을 수행해주는 연산자다.


    예제를 보고 학습해보자!


    #include <bitset>

    #include <iostream>

    using namespace std;


    int main()

    {

    // 임의의 색상 선언

    unsigned short color = 0x1234;


    // 녹색의 비트만 남긴다

    // 0x07e0은 2진수로 0000 0111 1110 0000

    unsigned short green_temp = color & 0x07e0;


    // 비트를 오른쪽으로 이동

    unsigned short green;

    green = green_temp >> 5;


    //출력

    cout << "color = " << bitset<16>(color) << "(" << color << ")\n";

    cout << "green_temp = " << bitset<16>(green_temp) << "(" << green_temp << ")\n";

    cout << "green = " << bitset<16>(green) << "(" << green << ")\n";


    return 0;

    }


    녹색 부분의 값이 잘 추출된 것을 볼 수 있다.


    자 그럼 마지막으로 빨간색의 값을 30으로 만들어보자.


    아까와 반대로 <<를 사용해서 왼쪽으로 옮기면 된다.


    바로 코드를 보자.


    #include <bitset>

    #include <iostream>

    using namespace std;


    int main()

    {

    // 임의의 한 색상

    unsigned short color = 0x1234;


    // color의 빨간색 부분의 비트들을 0으로 만든다

    unsigned short color_temp;

    color_temp = color & 0x07ff;


    // 새로운 빨간색의 값을 준비

    unsigned short red = 30;


    // 빨간색 값을 왼쪽 끝으로 옮긴다.

    unsigned short red_temp;

    red_temp = red << 11;


    // 빨간색 값을 색상에 넣는다

    unsigned short color_finished;

    color_finished = color_temp | red_temp;


    // 출력

    cout << " color = " << bitset<16>(color) << "(" << color << ")\n";

    cout << " color_temp = " << bitset<16>(color_temp) << "(" << color_temp << ")\n";

    cout << " red = " << bitset<16>(red) << "(" << red << ")\n";

    cout << " red_temp = " << bitset<16>(red_temp) << "(" << red_temp << ")\n";

    cout << " color_finished = " << bitset<16>(color_finished) << "(" << color_finished << ")\n";


    return 0;

    }



    R 값이 정상적으로 들어간 것을 볼 수 있다.



    부호 있는 값의 쉬프트


    지금까지 배운 쉬프트 연산자는 단순하게 비트들을 옮기고 0으로 채우는 쉬프트 방식이었다.


    하지만 값의 부호를 유지하면서 비트들을 옮기는 쉬프트가 있다.


    부호 있는 값의 쉬프트 예제를 보며 학습하자.


    #include <bitset>

    #include <iostream>

    using namespace std;


    int main()

    {

    unsigned short us = 0xff00;

    short s = (short)0xff00;


    unsigned short us_shift = us >> 4;

    short s_shift_r = s >> 4;


    cout << "us = " << bitset<16>(us) << "(" << us << ")\n";

    cout << "s = " << bitset<16>(s) << "(" << s << ")\n";

    cout << "us >> 4 = " << bitset<16>(us_shift) << "(" << us_shift << ")\n";

    cout << "s >> 4 = " << bitset<16>(s_shift_r) << "(" << s_shift_r << ")\n";


    return 0;

    }



    unsigned short 타입의 경우에는 새로 추가하는 비트의 값이 0인 반면에 signed short 타입의 경우에는 1의 값으로 설정한다.


    이러한 차이점이 생기는 이유는 컴퓨터가 부호를 유지하려 하기 때문이다.


    물론 음수의 값을 오른쪽으로 쉬프트 할 때만 1로 채워진다.



    연산자의 축약형


    지금까지 연산자를 배웠는데 더 간편하게 사용할 수 있는 방법이 있다!


    축약형 

    같은 의미의 수식 

    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; 


    그리고 ++과 --가 있다.


    ++과 --는 변수 앞에서 사용하느냐, 뒤에서 사용하느냐에 따라서 결과가 달라진다.


    예제를 보자!


    #include <iostream>

    using namespace std;


    int main()

    {

    int A, B, C, D;

    A = B = C = D = 5;


    int ppA, Bpp, mmC, Dmm;

    ppA = ++A; // 전치 연산

    Bpp = B++; // 후치 연산

    mmC = --C; // 전치 연산

    Dmm = D--; // 후치 연산


    cout << "A, B, C, D : " << A << ", " << B << ", " << C << ", " << D << "\n";

    cout << "++A, B++, --C, D-- : " << ppA << ", " << Bpp << ", " << mmC << ", " << Dmm << "\n";


    return 0;

    }



    전치 연산은 변수의 값을 증감시킨 후에 결과를 반환한다.


    반면에 후치 연산은 현재 변수의 값을 결과로써 반환한 후에 증감시킨다.



    연산 중에 발생하는 형변환


    다음과 같은 코드가 있다.


    long l;

    float f, result;


    result = l + f;


    피연산자 중 하나는 long 타입이고 다른 하나는 float 타입이다.


    규칙에 의하면 long 타입의 피연산자가 float 타입으로 형변환한 후에 계산이 된다.


    이렇게 피연산자의 타입이 동일하지 않은 경우에는 형변환을 통해서 타입을 일치시키는 규칙이 있다.


    이 규칙은 두 피연산자 중에서 더 큰 타입쪽으로 형변환이 발생한다.


    순서는 long double > double > float > unsigned long > long > unsigned int > int 이다.


    int 보다 작은 타입들은 정수형 승진이라는 과정을 통해서 자동적으로 int 혹은 unsigned int로 형변환한다.


    그리고 중요한 사실은 연산 중에 발생하는 형변환은 피연산자의 타입에만 영향을 받는다는 것이다.


    예제를 보자.


    #include <iostream>

    using namespace std;


    int main()

    {

    int i = 10;

    float f = 10.0f;


    float i_div_6, f_div_6;

    i_div_6 = i / 6;

    f_div_6 = f / 6;


    cout << "i = " << i << "\n";

    cout << "f = " << f << "\n";

    cout << "i / 6 = " << i_div_6 << "\n";

    cout << "f / 6 = " << f_div_6 << "\n";


    return 0;

    }



    연산 중의 형변환은 오직 피연산자의 타입에만 영향을 받음을 알 수 있다.


    다음 시간에는 조건문에 대해서 알아보자.

    'C++ 기본 문법 알아보기' 카테고리의 다른 글

    7장. 반복문  (0) 2019.01.10
    6장. 조건문이란?  (0) 2019.01.09
    5-1장. 기본적인 연산자  (0) 2019.01.07
    4장. 형변환이란?  (0) 2019.01.06
    3장. 타입에 대하여  (0) 2019.01.03
Designed by Tistory.