📚책읽기

[C/C++] 연산자, #define, 비트연산(Shift)

공대 컴린이 2022. 2. 9. 15:58
728x90
  • 대입 연산자: =
  • 산술 연산자
    • +, -, *, /, %(Modulus)
      % 는 '나머지 연산자'로써 피연산자가 모두 정수인 경우에만 사용 가능하다.
      int data = 10 % 3; // 10을 3으로 나눈 나머지 1

      / 는 나누기 연산자로 정수와 실수 모두 사용 가능하다.
      int data = 10. / 3. ;​
      위와 같은 수식을 적용할 때 10을 3으로 나눈 실제 연산 결과는 3.3333..이지만 data 변수에 저장되는 값은 data의 자료형인 int에 따라 정수형으로 '형 변환'되어 3으로 저장된다.

      * 실수를 상수로 적을 경우 소수점 뒤에 f를 붙이면 float 자료형(10.f)으로, f를 붙이지 않으면 double 자료형(10.)으로 인식한다.

    • 증감 연산자: ++, --
      증감 연산자는 변수의 수치를 한 단계 증가 또는 감소시킨다.
      int a = 10;
      data = a++;
       증감 연산자를 변수 뒤에 사용한 경우를 '후위(후치)'라고 말한다.
      data = ++a;​
      반대로 증감 연산자를 변수 앞에 사용한 경우는 '전위(전치)'라고 말한다.

      * 후위 연산자를 사용하는 경우엔 연산자의 우선순위가 모든 연산 중 가장 나중으로 실행되기 때문에 추후 작성될 수 있는 클래스나 구조체의 operator 함수를 호출할 때 좋지 못한 효율을 낼 수 있다. 
      따라서 웬만하면 '전위 연산자'를 사용하는 것이 좋다.
  • 논리 연산자
    • ! (역), && (곱, And), || (합, Or)
    • 참 (true), 거짓 (false)
      참: 0 이 아닌 값, 주로 1
      거짓: 0 
      • && : 곱 연산
        100 && 200 -> 참 && 참 -> 참(1)
        0 && 200 -> 거짓 && 참 -> 거짓(0)
      • || : 합 연산
        0 || 0 -> 거짓 || 거짓 -> 거짓(0)
        0 || 200 -> 거짓 || 참 -> 참(1)
    • 자료형: bool
      bool 자료형은 true(1) 혹은 false(0)로만 값이 저장되는 변수이다.
      굳이 실수와 정수 중 자료형을 따지자면 0과 1로 떨어지니 정수형이라고 볼 수 있지만 정수형 자료형으로써의 사용은 어렵다.
      bool isTrue = 100;​
       위와 같이 bool형 변수에 100이라는 숫자를 저장했을 때 isTrue 변수의 값을 확인하면 1로 저장되어 있다.
      100은 0이 아닌 값이기 때문에 참인 1로 저장되는 것이다.
      따라서 bool형 변수를 정수형 변수처럼 사용하는 것은 어렵다.
  • 비교 연산자
    • ==, !=, <, <=, >, >=, 참, 거짓 
    • 비교 구문
      • if, else if, else
      • switch - case - default
        switch 구문에서 case문 안에 break를 작성하지 않은 경우엔 하위 소스코드로 내려가며 break문을 만날 때까지 코드를 실행한다.
        switch(10) {
        case 10:
        	break;
        case 20:
            break; // 깜빡하고 break를 작성하지 않으면 아래로 내려가 default까지 실행한다.
        case 30:
        case 40:
        case 50:
            // 30,40,50인경우의 공통적인 실행 구문이 있을 수 있으므로
            // case 아래에 break가 없는 것은 잘못된 문법 오류가 아니다.
            // if (iTest == 30 || iTest == 40 || iTest == 50) 인 경우와 동일하게 작동한다.
            break;
        
        default:
            break;
        }​
  • 삼항 연산자
    • :?
      int iTest = 20;
      iTest == 20 ? iTest = 100 : iTest = 200;​
      iTest == 20이라는 조건문이 참이면 iTest = 100문의 연산을 수행하고, 조건문이 거짓이라면 iTest = 200문의 연산을 수행한다.
      삼항 연산자를 통해 조건문을 간략하게 작성할 수 있다는 장점이 존재하지만, 문맥을 한눈에 파악하기 힘들어 가독성이 떨어질 수 있다는 단점이 있다.
      따라서 정말 간략하게 코드를 작성하려는 의도가 아니라면 삼항 연산자를 굳이 사용하지 않는다.
  • #define 전처리기
    • define은 생성할 매크로를 지정한 값으로 치환해준다.
      #define MAX_SIZE 20​
      '#' 전처리기는 소스코드가 실행될 때 가장 먼저 실행되며 코드의 가독성을 높여주고 유지보수를 용이하게 돕는다.
  • 비트 연산자
    비트 연산자는 비트단위로 연산을 수행한다.
    • 쉬프트: <<, >>
      unsigned char byte = 1;	// 0000 0001
      byte <<= 1;		// 0000 0010
      byte 변수가 1일 때 비트는 '0000 0001'이 된다. 
      쉬프트 연산자를 사용하여 왼쪽으로 1칸씩 밀린다면 '0000 0010'이 되며 byte값은 2가 된다.

      그렇다면 왼쪽으로 2칸씩 밀린다면? '0000 0100' = 4
      처음 byte값 1에서 4가 된다.
      3칸씩 밀린다면 '0000 1000'이 되어 8이 된다.
      즉, 왼쪽 쉬프트 연산을 사용하면 2^n의 배수만큼 값이 증가하게 된다.

      반대로 오른쪽 쉬프트 연산자를 사용한다면 1/2, 1/4배가 될 것 같지만 비트가 오른쪽으로 밀리며 사라지는 경우가 발생할 수 있기 때문에 해당 가설은 이뤄질 수 없다.
      byte = 10; 	// 0000 1010
      byte >>= 1;	// 0000 0101
      byte >>= 1;	// 0000 0010​
      오른쪽으로 비트가 계속 밀린다면 처음 10이 저장되었던 byte 변수는 5가 되고 한 번 더 밀리면 2가 된다.
      즉, 오른쪽 쉬프트 연산은 n/2 배수가 아니라 2^n을 나눈 몫의 값이 도출된다.

    • 비트 곱(&), 합( | ), xor(^), 반전(~)
      • & : 둘 다 1인 경우 1
      • | : 둘 중 하나라도 1이면 1
      • ^ : 같으면 0, 다르면 1
        xor 연산은 기존에 알고 있는 true(1), false(0) 개념과는 반대되기 때문에 헷갈리기 쉽다.
      • ~ : 1은 0으로, 0은 1
    • 비트 연산의 활용
      • 게임 프로그램에서 캐릭터의 상태를 비트 연산으로 표현할 수 있다.
        // 게임에서 비트로 상태를 표현하기 위한 상태변수
        #define HUNGRY	0x001 // 0000 000'?' 첫번째 비트 자리
        #define THIRSTY 0x002 // 0000 00'?'0 두번째 비트 자리
        #define TIRED	0x004 // 0000 0'?'00 세번째 비트 자리
        #define FIRE	0x008
        
        #define COLD	0x010 // 16진수 표현 : 0x10 = 16
        #define POISON	0x020 // 0x20 = 32
        #define OOOOOO	0x040
        #define OOOOOO	0x080
        
        #define OOOOOO	0x100
        #define OOOOOO	0x200
        #define OOOOOO	0x400
        #define OOOOOO	0x800​
        위와 같이 게임 캐릭터의 상태를 매크로 변수로 저장해 두었다.
        해당 변수들은 비트 자릿수를 나타내며 32비트의 변수 하나를 생성했을 때 32개의 상태를 동시에 저장하게 된다. 
        * 실제 게임 소스코드에서는 위와 같이 16진수 표현을 이용한 규칙적인 비트 값으로 입력하여 사용한다.
      • 캐릭터 상태 설정
        unsigned int iStatus = 0;
        
        iStatus |= HUNGRY;
        iStatus |= THIRSTY;​
        32비트의 캐릭터 상태를 저장하는 iStatus변수를 생성한 뒤 비트 연산의 '합( | )'연산을 사용하면 HUNGRY 매크로가 갖는 비트 위치 (0000 000'1')를 1로 활성화하여 캐릭터가 배고픈 상태를 나타내도록 한다.
        같은 방식으로 THIRSTY 변수를 사용해 목마른 상태를 나타낼 수도 있다.
      • 캐릭터 상태 확인
        if(iStatus & THIRSTY) {
        	// 내 캐릭터가 현재 목마른 상태라면?
        }​
        비트 연산의 '곱(&)'연산을 이용해 캐릭터의 상태를 확인할 수 있다.
        THIRSTY가 아닌 다른 상태는 전혀 고려하지 않고 목마름의 상태만 확인하게 된다.

      • 캐릭터의 상태 변경 (특정 자리의 비트 제거)
        캐릭터가 목마름 상태에서 물을 마시면 다시 '목마르지 않음' 상태로 변경시켜야 한다.
        iStatus &= ~THIRSTY;​
        이때 위와같은 구문을 사용하면 목마름(THIRSTY)에 해당하는 비트 값만 반전시켜 캐릭터의 상태를 변경(해제)해준다. 
        위 구문의 진행 과정은 다음과 같다. (iStatus가 1110 1010 이라고 가정한다.)
        iStatus=	1110 1010
        THIRSTY=	0000 0010
        ~THIRSTY=	1111 1101
        곱연산(&)=	1110 1000​
         결과적으로 구문을 실행한 후 iStatus는 목마름 비트만 0으로 변경되었다.
        해당 구문은 실제로 많이 쓰이는 코드이기 때문에 '암기'하는 것이 좋다.

 

728x90