엔디안이란?
엔디안은 메모리에 저장된 멀티바이트 데이터의 바이트 순서(Byte Order)를 나타내는 개념이다. 같은 데이터를 저장할 때 바이트를 어떤 순서로 배치하느냐에 따라 해석이 달라진다.
엔디안이 적용되는 데이터 타입
적용되는 데이터 타입
엔디안은 1바이트보다 큰 데이터 타입에만 적용된다:
| 데이터 타입 | 크기 | 엔디안 적용 여부 |
|---|---|---|
char | 1바이트 | ❌ 적용 안 된다 |
short | 2바이트 | ✅ 적용됨 |
int | 4바이트 | ✅ 적용됨 |
long | 4바이트 (32비트) / 8바이트 (64비트) | ✅ 적용됨 |
long long | 8바이트 | ✅ 적용됨 |
float | 4바이트 | ✅ 적용됨 |
double | 8바이트 | ✅ 적용됨 |
pointer | 4바이트 (32비트) / 8바이트 (64비트) | ✅ 적용됨 |
핵심 원리
- 1바이트 데이터: 엔디안 문제가 없다. 바이트가 하나뿐이므로 순서가 의미가 없다.
- 멀티바이트 데이터: 2바이트 이상의 데이터는 바이트 순서에 따라 해석이 달라진다.
엔디안의 종류
Big Endian (빅 엔디안)
바이트를 낮은 주소부터 저장하는 방식이다.
메모리 주소: [0x1000] [0x1001] [0x1002] [0x1003]
데이터: [ 0x12] [ 0x34] [ 0x56] [ 0x78]
↑ 여기서 시작
- 사람이 읽기 쉬운 순서: 왼쪽에서 오른쪽으로 읽으면 자연스럽다.
- 사용 예시: 네트워크 프로토콜(IP 주소 등), 일부 프로세서(ARM의 일부 모드, PowerPC 등)
Little Endian (리틀 엔디안)
바이트를 높은 주소부터 저장하는 방식이다.
메모리 주소: [0x1000] [0x1001] [0x1002] [0x1003]
데이터: [ 0x78] [ 0x56] [ 0x34] [ 0x12]
↑ 여기서 시작
- 메모리 효율성: 산술연산은 하위 바이트에서 시작하기 때문에 리틀 엔디안 방식은 cpu 연산에 유리하다.
- 사용 예시: x86/x64 아키텍처, 대부분의 개인용 컴퓨터
뒤집는 단위와 범위
뒤집는 단위: 바이트(Byte) 단위
엔디안은 바이트 단위로 뒤집힌다. 비트 단위가 아니다.
예시: 0x12345678 (4바이트 정수)
Big Endian:
메모리 주소: 0x1000 0x1001 0x1002 0x1003
바이트 값: 0x12 0x34 0x56 0x78
Little Endian:
메모리 주소: 0x1000 0x1001 0x1002 0x1003
바이트 값: 0x78 0x56 0x34 0x12
각 바이트 내부의 비트 순서는 변하지 않는다. 바이트 단위로만 순서가 바뀐다.
뒤집는 범위: 전체 데이터 타입의 크기
엔디안 변환은 데이터 타입의 전체 크기 범위에 적용된다.
2바이트 데이터 (short)
short value = 0x1234; // 2바이트
Big Endian:
[0x12] [0x34]
Little Endian:
[0x34] [0x12]
4바이트 데이터 (int)
int value = 0x12345678; // 4바이트
Big Endian:
[0x12] [0x34] [0x56] [0x78]
Little Endian:
[0x78] [0x56] [0x34] [0x12]
8바이트 데이터 (long long)
long long value = 0x1234567890ABCDEF; // 8바이트
Big Endian:
[0x12] [0x34] [0x56] [0x78] [0x90] [0xAB] [0xCD] [0xEF]
Little Endian:
[0xEF] [0xCD] [0xAB] [0x90] [0x78] [0x56] [0x34] [0x12]
실제 예제
C 코드로 확인하기
#include <stdio.h>
int main() {
int value = 0x12345678;
unsigned char *bytes = (unsigned char *)&value;
printf("Value: 0x%08X\n", value);
printf("Memory layout:\n");
for (int i = 0; i < sizeof(int); i++) {
printf(" [%p]: 0x%02X\n", &bytes[i], bytes[i]);
}
return 0;
}
4바이트 int를 Little Endian으로 변환하는 toBytes 함수
#include <stdio.h>
#include <stdint.h>
void toBytes(uint32_t value, unsigned char *bytes) {
// Little Endian: 낮은 주소부터 LSB부터 저장
bytes[0] = (value >> 0) & 0xFF; // LSB (0x78)
bytes[1] = (value >> 8) & 0xFF; // 0x56
bytes[2] = (value >> 16) & 0xFF; // 0x34
bytes[3] = (value >> 24) & 0xFF; // MSB (0x12)
}
int main() {
uint32_t value = 0x12345678;
unsigned char bytes[4];
toBytes(value, bytes);
printf("Value: 0x%08X\n", value);
printf("Little Endian bytes:\n");
for (int i = 0; i < 4; i++) {
printf(" bytes[%d] = 0x%02X\n", i, bytes[i]);
}
// 출력:
// Value: 0x12345678
// Little Endian bytes:
// bytes[0] = 0x78
// bytes[1] = 0x56
// bytes[2] = 0x34
// bytes[3] = 0x12
return 0;
}
엔디안이 중요한 상황
1. 네트워크 통신
네트워크 프로토콜은 **Big Endian(Network Byte Order)**을 표준으로 사용한다. 서로 다른 엔디안을 가진 시스템 간 통신 시 변환이 필요하다.
// 네트워크 바이트 순서로 변환
uint32_t host_value = 0x12345678;
uint32_t network_value = htonl(host_value); // Host TO Network Long
2. 파일 포맷
바이너리 파일 포맷은 특정 엔디안을 가정한다. 예를 들어:
- PNG: Big Endian
- GIF: Little Endian
- JPEG: Big Endian
3. 프로세서 간 데이터 공유
서로 다른 엔디안을 가진 프로세서 간 데이터를 공유할 때 변환이 필요하다.
4. 디버깅
메모리 덤프를 읽을 때 엔디안을 고려해야 올바르게 해석할 수 있다.
요약
| 항목 | 설명 |
|---|---|
| 적용 데이터 타입 | 1바이트보다 큰 모든 멀티바이트 데이터 타입 (short, int, long, float, double, pointer 등) |
| 뒤집는 단위 | 바이트(Byte) 단위 (비트 단위 아님) |
| 뒤집는 범위 | 데이터 타입의 전체 크기 (2바이트면 2바이트 전체, 4바이트면 4바이트 전체) |
| 핵심 원리 | 각 바이트 내부의 비트 순서는 변하지 않고, 바이트들의 순서만 바뀜 |
추가 참고사항
- 엔디안 중립 코드 작성: 네트워크 통신이나 파일 I/O 시에는 항상 엔디안 변환 함수(
htonl,ntohl등)를 사용해야 한다. - 현대 시스템: 대부분의 개인용 컴퓨터는 Little Endian을 사용하지만, 네트워크 표준은 Big Endian이므로 변환이 필요하다.