한양과학기술고등학교

에듀테크 프로젝트 대시보드

엔디언 (Endianness)

  엔디언 (Endianness)

숫자 하나가 메모리에 저장될 때, 여러 바이트를 어떤 순서로 늘어놓을까?
큰 자리(상위 바이트)를 낮은 주소에 두면 빅 엔디언, 작은 자리(하위 바이트)를 낮은 주소에 두면 리틀 엔디언. 같은 값이라도 메모리 덤프를 보면 바이트 순서가 뒤집혀 보이는 이유가 여기 있다.

값을 메모리에 배치해 보기

값 = 305419896 = 0x 12345678

빅 엔디언 Big-Endian

상위(큰 자리) 바이트가 낮은 주소로. 사람이 숫자 읽는 순서와 같다. 네트워크 표준 순서.

리틀 엔디언 Little-Endian

하위(작은 자리) 바이트가 낮은 주소로. x86 · ARM(기본) · RISC-V가 쓴다.

왜 두 방식이 생겼을까

리틀 엔디언의 장점

낮은 주소부터 읽으면 곧바로 하위 바이트가 나온다. 그래서 int를 short로, long을 int로 캐스팅할 때 주소를 그대로 두고 길이만 줄이면 된다. 덧셈·곱셈의 자리올림(carry)도 낮은 자리부터 처리하니 자연스럽다.

빅 엔디언의 장점

메모리 덤프를 왼쪽부터 읽으면 사람이 쓰는 숫자 순서 그대로 보인다. 부호·크기 비교를 앞 바이트부터 할 수 있어 문자열 정렬 같은 사전식 비교에 유리하다. 그래서 네트워크 프로토콜의 표준(“network byte order”)으로 채택됐다.

네트워크 바이트 순서

서로 다른 CPU가 통신하려면 약속이 필요하다. IP·TCP 헤더의 포트·주소 필드는 모두 빅 엔디언. C에서는 htons()·htonl()(host→network), ntohs()·ntohl()로 변환한다.

바이 엔디언 / 함정

ARM·PowerPC는 부팅 시 엔디언을 고를 수 있는 bi-endian. 파일 포맷(PNG는 빅, BMP는 리틀), WAV·FAT도 제각각이라, 바이너리를 파싱할 땐 스펙에 적힌 엔디언을 꼭 확인해야 한다.

내 컴퓨터는 어느 쪽일까 (코드)

# Python — 시스템 엔디언 확인
import sys, struct
print(sys.byteorder)            # 'little' 또는 'big'

n = 0x12345678
print(struct.pack('>I', n).hex())   # '12345678'  (> = big-endian)
print(struct.pack('<I', n).hex())   # '78563412'  (< = little-endian)

# 되돌리기: bytes -> int
print(int.from_bytes(bytes.fromhex('78563412'), 'little'))  # 305419896
// C — 포인터로 첫 바이트 엿보기
uint32_t x = 0x12345678;
unsigned char *p = (unsigned char*)&x;
// 리틀 엔디언: p[0]==0x78   빅 엔디언: p[0]==0x12
printf("%s\n", p[0] == 0x12 ? "big" : "little");

0x0A0B0C0D 를 주소 1000~1003 에 저장하면

주소1000100110021003
빅 엔디언0A0B0C0D
리틀 엔디언0D0C0B0A

최상위 바이트 0A 가 빅 엔디언에서는 맨 앞, 리틀 엔디언에서는 맨 뒤에 온다.

덤프 읽기 퀴즈

아래 4바이트가 리틀 엔디언으로 저장된 uint32 라면, 원래 값은?

2C 01 00 00

핵심: 엔디언은 바이트 단위 순서일 뿐, 한 바이트 안의 비트 순서는 바꾸지 않는다. 같은 메모리라도 어떤 렌즈(엔디언)로 보느냐에 따라 다른 숫자가 된다.