signed와 unsigned
signed(부호 있는)와 unsigned(부호 없는)는 컴퓨터 과학에서 정수형 데이터 타입이 부호(양수/음수)를 표현할 수 있는지 여부를 구분하는 분류이다. 이 구분은 메모리 내 비트 패턴의 해석 방식과 표현 가능한 수의 범위를 결정한다.
1. signed (부호 있는 정수)
- 정의: 최상위 비트(MSB, Most Significant Bit)를 부호 비트(Sign Bit)로 사용하여 양수, 0, 음수를 모두 표현하는 방식이다.
- 표현 방식: 현대 컴퓨터 시스템에서는 2의 보수(Two's Complement) 방식이 사실상 표준으로 사용된다. 1의 보수나 부호-크기 방식이 역사적으로 존재했으나 현재는 거의 쓰이지 않는다.
- 범위: n비트 정수일 때 표현 범위는 $-2^{n-1}$ 부터 $2^{n-1}-1$ 까지이다.
- 예: 8비트 signed 정수(int8_t)의 범위는 -128 ~ 127이다.
- 오버플로: 표현 범위를 초과하는 연산 발생 시 정의되지 않은 행동(Undefined Behavior, C/C++ 표준) 또는 래핑(Wrapping, Java 등) 등이 발생할 수 있다.
2. unsigned (부호 없는 정수)
- 정의: 모든 비트를 크기(Magnitude)를 나타내는 데 사용하여 0과 양수만 표현하는 방식이다. 부호 비트가 별도로 존재하지 않는다.
- 표현 방식: 순수 이진법(Pure Binary)으로 해석한다.
- 범위: n비트 정수일 때 표현 범위는 $0$ 부터 $2^n-1$ 까지이다.
- 예: 8비트 unsigned 정수(uint8_t)의 범위는 0 ~ 255이다.
- 오버플로: 산술 연산 시 모듈로 $2^n$ 연산(Modulo Arithmetic, 래핑)으로 정의되어 있는 경우가 많다(C/C++ 표준 보장).
3. 주요 차이점 및 비교
| 특징 | Signed | Unsigned |
|---|---|---|
| 표현 수 | 음수, 0, 양수 | 0, 양수 |
| 최상위 비트(MSB) | 부호 비트 (1: 음수, 0: 양수/0) | 데이터 비트 (값의 일부) |
| 동일 비트 폭에서 최대값 | 약 절반 ($2^{n-1}-1$) | 약 2배 ($2^n-1$) |
| 비교 연산 | 산술 비교 (Arithmetic Comparison) | 논리 비교 (Logical Comparison) |
| 오른쪽 시프트(>>) | 산술 시프트 (부호 비트 복사, 구현 정의) | 논리 시프트 (0 채움) |
4. 프로그래밍 언어에서의 사용
- C/C++:
int,short,long,char등은 구현 정의(보통 signed)이며, 명시적으로signed int,unsigned int등으로 지정 가능.char의 부호 여부는 컴파일러/아키텍처 의존적. - Java:
byte,short,int,long은 모두 signed만 지원.char는 unsigned 16비트 유니코드 문자용. Java 8부터Integer.toUnsignedString등 unsigned 연산 지원 메서드 제공. - C#:
sbyte,short,int,long(signed) /byte,ushort,uint,ulong(unsigned) 키워드 별도 제공. - Rust:
i8~i128,isize(signed) /u8~u128,usize(unsigned) 명시적 구분. - Go:
int8~int64,int(signed) /uint8~uint64,uint(unsigned) 명시적 구분. - Python 3: 정수 타입(
int)은 부호가 있으며 임의 정밀도(Arbitrary Precision)를 지원하여 오버플로가 발생하지 않음. 별도 unsigned 타입 없음.
5. 타입 변환 및 승격 (Type Conversion/Promotion)
- 부호 있는/없는 타입 혼용 연산: C/C++ 등에서 signed와 unsigned 피연산자가 섞이면, 보통 unsigned로 승격(Integer Promotion)되어 연산이 수행된다. 이 경우 의도치 않은 결과(예: 음수가 매우 큰 양수로 해석됨)가 발생할 수 있어 주의가 필요하다.
- 명시적 캐스팅: 타입 변환 시 비트 패턴은 유지되지만 해석 방식이 바뀐다. (예:
uint8_t255(0xFF)를int8_t로 캐스팅 시 -1이 됨).
6. 용례 및 선택 가이드
- Signed 사용: 일반적인 산술 연산, 음수 값이 의미 있는 데이터(온도, 좌표, 차이값 등), 인덱스가 음수가 될 수 있는 알고리즘.
- Unsigned 사용: 비트 조작, 메모리 주소/크기(
size_t), 카운터(루프 변수 등, 단 감소 연산 시 언더플로 주의), 비트마스크, 네트워크 패킷/파일 포맷 파싱 등 원시 데이터 처리. - 모범 사례: 많은 코딩 가이드라인(C++ Core Guidelines 등)은 "음수가 필요하지 않더라도
unsigned를 남용하지 말고, 기본적으로signed(int등)를 사용하라"고 권고하기도 한다. 부호 없는 타입과의 혼용으로 인한 버그를 방지하기 위함이다. 반면, 임베디드 시스템이나 비트 단위 연산이 빈번한 도메인에서는unsigned사용이 표준적이다.