엔디안
개념
멀티 바이트(2바이트, 4바이트, 8바이트) 데이터를 메모리에 저장할 때 어떤 순서로 저장할지 정의한 것
- 빅 엔디안(Big Endian): 큰 바이트(MSB, Most Significant Byte)가 메모리의 앞 주소(가장 낮은 주소)에 저장됨.
- 리틀 엔디안(Little Endian): 작은 바이트(LSB, Least Significant Byte)가 메모리의 앞 주소(가장 낮은 주소)에 저장됨.
MSB는 가장 큰 값, LSB는 가장 작은 값으로 생각하면 된다.
0x12345678을 기준으로 생각하면,0x12가 가장 큰 값을 가지고,0x78은 가장 작은 값을 가진다.
1바이트는 16진수 2자리(00~FF)이다. Short는 2바이트, Long은 4바이트이다.
빅 엔디안
4바이트 값 0x12345678이 있을 때 빅 엔디안은 아래와 같이 저장된다.
| 주소 | 값 |
|---|---|
| 0x100 | 0x12(MSB) |
| 0x101 | 0x34 |
| 0x102 | 0x56 |
| 0x103 | 0x78(LSB) |
특징
- 빅 엔디안은 사람이 숫자를 읽는 순서와 동일해서 읽기 쉽다.
- 네트워크 표준 byte order (TCP/IP는 빅 엔디안 사용)
리틀 엔디안
4바이트 값 0x12345678이 있을 때 리틀 엔디안은 아래와 같이 저장된다.
| 주소 | 값 |
|---|---|
| 0x100 | 0x78(LSB) |
| 0x101 | 0x56 |
| 0x102 | 0x34 |
| 0x103 | 0x12(MSB) |
특징
- CPU 연산 효율이 좋다.
- 대부분의 PC CPU가 사용한다.
- x86
- x86-64
- ARM
필요성
CPU는 1바이트씩이 아닌 여러 바이트를 하나의 숫자로 처리한다.
0x12345678 값을 메모리에 저장할 때 어떤 바이트부터 저장할 지, 어떤 순서로 읽을지 CPU 아키텍처마다 약속이 필요한데, 이를 Endianness라고 한다.
네트워크에서 중요한 이유
네트워크 장비나 프로토콜에서는 빅 엔디안을 사용하는데, 리틀 엔디안 CPU에서는 빅 엔디안을 리틀 엔디안으로 변환해서 해석해야 하기 때문에 아래와 같은 함수가 필요하다.
1
2
3
4
htons() // host to network short
htonl() // host to network long
ntohs()
ntohl()
만약, 리틀 엔디안 CPU에서 0x1234 를 네트워크로 전송하면 0x3412로 변환된다.
네트워크에서 빅 엔디안이 표준인 이유
인터넷 프로토콜(TCP/IP)을 설계한 시기는 1970~80년대인데, 당시 많이 사용되던 CPU들은 대부분 빅 엔디안 구조였다. 그래서 자연스럽게 빅 엔디안을 기준으로 프로토콜을 정의했다. 이후 RFC 표준화를 통해 Network Byte Order는 빅 엔디안으로 정의되었고, 빅 엔디안은 모든 장비와 호환성을 유지하기 위해 사용된다.
작동 원리
리틀 엔디안 CPU에서 0x1234 는 메모리에 아래와 같이 저장된다.
| 주소 | 값 |
|---|---|
| 0x100 | 0x34 |
| 0x101 | 0x12 |
네트워크로 전송할 때는 메모리의 낮은 주소부터 순서대로 전송한다. 하지만 위와 같은 상태로 네트워크에 전송하면 0x3412 로 인식된다. 그렇기 때문에 htons() 함수를 사용해서 빅 엔디안으로 변환해주어야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stdint.h>
#include <arpa/inet.h>
int main(void)
{
uint16_t x = 0x1234;
uint16_t y = htons(x);
printf("x: %#x, y: %#x\n", x, y);
return 0;
}
실행 결과는 아래와 같다.
1
x: 0x1234, y: 0x3412
htons()실행 결과로 0x1234 값이 0x3412가 되었고, 리틀 엔디안 CPU를 사용하는 메모리에는 다음과 같이 저장되어있는 것이다.
| 주소 | 값 |
|---|---|
| 0x100 | 0x12 |
| 0x101 | 0x34 |