C언어를 처음 배울 때 사칙연산은 익숙하게 받아들이지만, 비트 연산자는 많은 사람이 어렵게 느낀다.
기호도 낯설고, 숫자를 2진수로 생각해야 하며, 코드도 직관적으로 보이지 않기 때문이다.
특히 임베디드, AUTOSAR, 차량 제어 소프트웨어를 보면 아래와 같은 코드가 자주 등장한다.
Status |= 0x01U;
Status &= ~0x04U;
Signal = (RxData[0] >> 2U) & 0x01U;
처음에는 암호처럼 보일 수 있다.
하지만 비트 연산자는 원리만 이해하면 매우 단순하며, 실무에서는 오히려 가장 자주 보는 핵심 기술 중 하나다.
왜냐하면 임베디드 시스템은 메모리가 제한적이고, 빠른 처리 속도가 중요하며,
하드웨어 제어를 직접 해야 하는 경우가 많기 때문이다.
이때 비트 단위 제어는 매우 효율적이다.
이번 글에서는 비트가 무엇인지부터, 각 연산자의 동작 원리, 그리고 실제 실무 코드에서 왜 사용하는지까지 자세히 설명한다.
비트(Bit)란 무엇인가?
컴퓨터는 모든 데이터를 0과 1로 저장한다.
숫자, 문자, 이미지, 센서값 모두 내부적으로는 결국 0과 1의 조합이다.
이때 0 또는 1 한 칸을 비트(Bit) 라고 부른다.
예를 들어 uint8 자료형은 8개의 비트로 구성된다.
Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0
0 0 0 0 0 0 0 0
즉, 총 8칸이 있으며 각 칸은 0 또는 1 값을 가진다.
숫자가 비트로 저장되는 방식
예를 들어 아래 코드가 있다고 하자.
uint8 Value = 5U;
숫자 5를 2진수로 바꾸면 다음과 같다.
0000 0101
왜 5가 이렇게 될까?
2^0 = 1
2^2 = 4
즉:
4 + 1 = 5
그래서 Bit2 와 Bit0 이 1이 된다.
왜 비트 단위로 제어할까?
일반 변수 하나에 상태 하나만 저장하면 메모리를 많이 사용하게 된다.
예를 들어 아래 상태가 있다고 하자.
- Motor ON/OFF
- Error 발생 여부
- Warning 발생 여부
- Auto Wash Mode 여부
- Sleep Mode 여부
이 상태를 각각 변수로 만들 수도 있다.
uint8 MotorOn;
uint8 Error;
uint8 Warning;
uint8 AutoWashMode;
uint8 SleepMode;
하지만 비트 연산을 사용하면 uint8 하나로 최대 8개 상태를 관리할 수 있다.
Bit0 = MotorOn
Bit1 = Error
Bit2 = Warning
Bit3 = AutoMode
Bit4 = SleepMode
즉, 메모리 절약, 빠른 처리, 상태 관리 편리 등의 이유로 실무에서 널리 사용된다.
비트 연산자 종류
| 연산자 | 이름 | 이름 설명 | 주요 용도 |
| & | AND | 둘 다 1이면 1 | 특정 비트 확인 |
| | | OR | 하나라도 1이면 1 | 특정 비트 켜기 |
| ^ | XOR | 서로 다르면 1 | 비트 토글 |
| ~ | NOT | 0은 1로, 1은 0으로 | 비트 반전 |
| << | Left Shift | 비트를 왼쪽으로 이동 | 비트 이동 / 마스크 생성 |
| >> | Right Shift | 비트를 오른쪽으로 이동 | 비트 추출 |
이제 하나씩 자세히 보자.
1. AND 연산자 '&'
AND는 두 비트가 모두 1일 때만 결과가 1이 된다.
1 & 1 = 1
1 & 0 = 0
0 & 1 = 0
0 & 0 = 0
즉, 조건이 엄격하다. 둘 다 1이어야 한다.
# 실무 예제
if ((Status & 0x04U) != 0U)
{
/* Error 활성화 */
}
Bit2 가 1인지 검사하고, 1이면 에러 상태로 하는 코드이다.
보통 센서 오류, 통신 상태, 스위치 입력 검사에 자주 사용된다.
2. OR 연산자 '|'
둘 중 하나라도 1이면 결과가 1이다.
1 | 0 = 1
0 | 1 = 1
1 | 1 = 1
0 | 0 = 0
# 실무 예제
uint8 Status = 0x00U;
Status = Status | 0x02U;
OR 연산을 해보면...
00000000
00000010
---------------
00000001
이 코드는 Bit1을 ON 한다.
모터 동작 시작, Warning 상태 Set, 진단 플래그 활성화를 할 때 자주 사용한다.
기존 다른 비트는 유지하면서 원하는 비트만 켤 수 있다.
3. NOT 연산자 '~'
0은 1로, 1은 0으로 뒤집는다.
00000100
11111011
즉, 특정 마스크를 반전할 때 자주 사용한다.
# 실무 예제
Status &= ~0x04U;
동작 순서:
1. 0x04U = Bit2만 1
2. ~0x04U = Bit2만 0, 나머지는 1
3. AND 수행
결과적으로 Bit2만 OFF 된다.
이 패턴은 Error Clear, Wakeup Flag 해제할 때 가끔 사용된다.
4. XOR 연산자 '^'
두 비트가 서로 다르면 1이다.
1 ^ 0 = 1
0 ^ 1 = 1
1 ^ 1 = 0
0 ^ 0 = 0
# 실무 예제
Status ^= 0x01U;
의미:
Bit0가 0이면 1
Bit0가 1이면 0
즉, 상태를 반전한다.
버튼 누를 때 ON/OFF 전환, LED 토글, 테스트 모드 전환 등을 할 때 활용된다.
5. 왼쪽 시프트 '<<'
비트를 왼쪽으로 이동한다.
uint8 Value = 1U;
Value = Value << 3U;
Value 초기값이 1 = 0000 0001 인데 왼쪽으로 3비트 이동하라는 코드이다.
즉, 0000 1000 가 되어 결과는 8이다.
왜 사용하는가?
원하는 위치의 비트를 만들기 위해 사용한다.
6. 오른쪽 시프트 '>>'
비트를 오른쪽으로 이동한다.
uint8 Value = 0x08U;
Value = Value >> 3U;
Value 초기값이 8 = 0000 1000 인데 오른쪽으로 3비트 이동하라는 코드이다.
즉, 0000 0001 이 되어 결과는 1이다.
# 실무 예제
uint8 Data = 0x08U;
uint8 Signal;
Signal = (Data >> 3U) & 0x01U;
설명:
Data의 모든 비트를 3칸 오른쪽으로 이동한다.
(원래 Bit3 값이 최하위 비트(Bit0) 위치로 내려옴)
'& 0x01U' 를 통해 최하위 1비트만 남긴다.
결과:
Signal = 1U;
차량 통신 메시지 파싱에서 매우 흔한 방식이다.
AUTOSAR / 임베디드 실무 예제
예제 1: 진단 상태 관리
uint8 DiagStatus = 0U;
/* Error 발생 */
DiagStatus |= 0x01U;
/* Warning 발생 */
DiagStatus |= 0x02U;
/* Error 해제 */
DiagStatus &= ~0x01U;
하나의 변수로 여러 진단 상태를 관리한다.
예제 2: 입력 스위치 판별
uint8 InputState = 0x04U;
if ((InputState & 0x04U) != 0U)
{
/* Switch ON */
}
특정 입력 비트 확인.
예제 3: CAN 데이터 파싱
uint8 RxData[8];
uint8 DoorOpen;
DoorOpen = RxData[0] & 0x01U;
첫 번째 바이트의 Bit0 값을 읽는다.
정리
임베디드 시스템에서는 메모리를 아끼고, 상태를 효율적으로 관리하며,
하드웨어 데이터를 해석하기 위해 반드시 필요한 기술이다.
처음에는 복잡해 보여도 결국 핵심은 단 하나다.
비트 연산자는 특정 비트를 읽고, 켜고, 끄고, 이동하는 기술이다.
'C 언어 > 실무' 카테고리의 다른 글
| C언어 내부 변수 / 전역 변수 / static 변수 사용 기준 (0) | 2026.04.16 |
|---|---|
| C언어 구조체 실무 예제 (왜 구조체를 쓰는가?) (0) | 2026.04.15 |
| C언어 extern 변수 사용법 (파일 간 변수 공유 방법) (0) | 2026.04.13 |
| C언어 배열과 포인터 차이 (헷갈리는 이유부터 핵심 차이까지) (0) | 2026.04.12 |
| C언어 포인터 쉽게 설명하기 (개념부터 실무 예제까지 한 번에) (4) | 2026.04.11 |