본문으로 건너뛰기

비트 연산자와 매크로

비트 논리 연산자 (Bitwise Logical Operators) *복습

**Bitwise Logical Operator(비트 논리 연산자)**는 정수 값을 비트 단위로 논리 연산을 수행하는 연산자이다. C 언어에는 &(AND), |(OR), ^(XOR), ~(Complement)의 네 가지가 있다.

연산자기호의미
AND&두 피연산자의 각 비트가 모두 1일 때만 1을 반환한다.
OR``
XOR^두 피연산자의 각 비트가 서로 다를 때 1을 반환한다.
Complement~단일 피연산자의 모든 비트를 반전시킨다(1의 보수).

예를 들어, 정수 3과 5에 대해 & 연산을 수행하면 다음과 같다.

  • 3의 32비트 이진수: 00000000 00000000 00000000 00000011

  • 5의 32비트 이진수: 00000000 00000000 00000000 00000101

  • 3 & 5의 결과: 00000000 00000000 00000000 00000001 (정수 1)

이동 연산자 (Shift Operators) *복습

**Shift Operator(이동 연산자)**는 변수의 비트들을 지정된 횟수만큼 왼쪽 또는 오른쪽으로 이동시키는 연산자이다.

연산자기호의미
왼쪽 이동<<비트를 왼쪽으로 이동시킨다.
오른쪽 이동>>비트를 오른쪽으로 이동시킨다.
  • 왼쪽 이동 (<<): 비트를 왼쪽으로 이동시키고, 왼쪽 경계를 벗어나는 비트는 버려진다. 오른쪽의 빈자리는 모두 0으로 채워진다. a << b 연산은 a2^b를 곱하는 효과와 같다.

  • 오른쪽 이동 (>>): 비트를 오른쪽으로 이동시키고, 오른쪽 경계를 벗어나는 비트는 버려진다. 왼쪽의 빈자리는 원래 숫자의 부호 비트(최상위 비트)로 채워진다 (산술 시프트). 피연산자가 양수일 때 a >> b연산은 a2^b로 나누는 효과와 같다.

비트 회전 (Rotate)

비트 **Rotate(회전)**는 이동 연산과 달리 한쪽 끝에서 밀려나는 비트를 다른 쪽 끝으로 다시 채워 넣는 연산이다.

  • rotateLeft(num, n): (num << n) | (num >> (16-n)) (16비트 기준)

  • rotateRight(num, n): (num >> n) | (num << (16-n)) (16비트 기준)

비트 마스크 (Bit Mask) *복습

**Bit Mask(비트 마스크)**는 특정 비트들만 선택하거나 값을 변경하기 위해 사용하는 상수나 변수이다.

  • 특정 비트 값 확인: 확인하려는 비트 위치만 1이고 나머지는 0인 마스크를 만들어 & 연산을 수행한다.

    예를 들어, 변수 a의 n번째 비트를 확인하는 일반식은 (a & (1 << (n-1)))이다.

  • 특정 비트 값 변경:

    • 비트를 1로 설정 (Set): | 연산을 사용한다.

    • 비트를 0으로 설정 (Clear): 해당 비트만 1인 마스크를 ~로 반전시킨 후 & 연산을 사용한다.

    • 비트 반전 (Toggle): ^ 연산을 사용한다.

매크로와 전처리기

시스템 정의 매크로

C언어는 디버깅 등의 편의를 위해 시스템에서 미리 정의된 매크로를 제공한다.

매크로기능
__FILE__현재 컴파일 중인 파일의 이름을 나타낸다.
__TIME__소스 파일이 컴파일된 시간을 시:분:초 형식으로 나타낸다.
__DATE__소스 파일이 컴파일된 날짜를 년/월/일 형식으로 나타낸다.
__LINE__해당 매크로가 위치한 소스 코드의 줄 번호를 나타낸다.

조건부 컴파일

전처리기 지시자를 사용하여 특정 조건에 따라 코드의 일부를 컴파일에 포함시키거나 제외시킬 수 있다.

  • #if, #elif, #else, #endif: 상수 수식을 평가하여 조건에 따라 코드를 포함한다.

  • #ifdef: defined의 약자로, 뒤따르는 기호상수(매크로)가 정의되어 있으면 코드를 포함한다.

  • #ifndef: if not defined의 약자로, 뒤따르는 기호상수가 정의되어 있지 않으면 코드를 포함한다.

  • defined 연산자: #if defined(DEBUG)와 같이 사용하여 #ifdef DEBUG와 동일한 기능을 수행할 수 있다.

전처리기를 이용한 디버깅

전처리기는 디버깅용 코드를 효율적으로 관리하는 데 매우 유용하다.

  • 컴파일러 옵션 활용: 소스 코드에 #define DEBUG를 직접 작성하는 대신, 컴파일러 명령어 옵션(cl -DDEBUG ...)으로 매크로를 정의할 수 있다. 이를 통해 소스 코드 수정 없이 디버그 모드를 켜고 끌 수 있다.

  • 디버깅 코드 분리: #ifdef DEBUG 또는 #if defined(DEBUG)를 사용하여 디버깅용 printfcout 문장들을 감싸면, DEBUG 매크로가 정의되지 않았을 때 해당 코드들이 최종 실행 파일에서 완전히 제거되어 프로그램의 성능에 영향을 주지 않는다.

  • 디버깅용 매크로 함수: 디버깅 코드를 매크로 함수로 만들어 코드를 더 깔끔하게 관리할 수 있다.

    #if defined(DEBUG)
    #define DebugCode(code_fragment) { code_fragment }
    #else
    #define DebugCode(code_fragment) // 아무것도 하지 않음
    #endif

    // 사용 예시
    DebugCode(cout << "Selected = " << select << endl;);