임베디드 면접 용어 사전
C 언어endian · 리틀 엔디안

엔디안

다바이트 값을 메모리에 놓는 순서. 통신·파일 포맷에서 바이트 순서를 맞추지 않으면 값이 뒤집힌다.

여러 바이트로 이루어진 값을 메모리에 어떤 순서로 저장하는지에 대한 규칙.

두 방식

0x12345678 (4바이트)을 주소 0x1000부터 저장하면

              0x1000  0x1001  0x1002  0x1003
리틀 엔디안     78      56      34      12      ← 낮은 자리가 낮은 주소
빅 엔디안       12      34      56      78      ← 높은 자리가 낮은 주소

빅 엔디안이 사람이 읽는 순서와 같다. 리틀 엔디안은 거꾸로 보인다.

이름의 유래

걸리버 여행기에서 달걀을 큰 쪽(big end) 부터 깨느냐 작은 쪽(little end) 부터 깨느냐로 다투는 이야기에서 왔다. "어느 쪽이든 상관없는데 다툰다"는 풍자가 담겨 있다 — 실제로 기술적 우열은 거의 없다.

어디가 어느 쪽인가

리틀 엔디안빅 엔디안
x86 / x64네트워크 바이트 순서
ARM (기본)옛 PowerPC, SPARC, m68k
RISC-VTCP/IP 헤더, 대부분의 프로토콜

ARM은 바이엔디안(bi-endian) 이라 설정으로 바꿀 수 있지만 현실적으로 거의 모두 리틀 엔디안으로 동작한다.

리틀 엔디안의 실질적 장점

uint32_t x = 0x12345678;
uint8_t  b = *(uint8_t *)&x;      // 리틀: 0x78 (하위 바이트)

타입을 좁혀 캐스팅할 때 주소가 그대로다. 32비트 값의 하위 8비트를 읽으려면 같은 주소에서 1바이트만 읽으면 된다. 빅 엔디안은 주소를 조정해야 한다.

빅 엔디안은 덤프를 눈으로 읽기 쉽다는 장점이 있다.

확인 방법

int is_little(void) {
    uint16_t x = 1;
    return *(uint8_t *)&x == 1;    // 리틀이면 하위 바이트(1)가 먼저
}

// 컴파일 시 판별 (GCC)
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__

문제가 되는 곳

① 네트워크 통신

// 네트워크 바이트 순서 = 빅 엔디안 (RFC 1700)
uint16_t port = htons(8080);      // host to network short
uint16_t p    = ntohs(recv_port); // network to host short

리틀 엔디안 기기에서 htons는 바이트를 뒤집고, 빅 엔디안 기기에서는 아무것도 하지 않는다. 양쪽 모두 같은 코드를 쓸 수 있다는 것이 요점이다.

② 서로 다른 MCU 간 통신

struct Packet { uint32_t id; uint16_t value; };
send(&pkt, sizeof(pkt));   // ✗ 상대가 다른 엔디안이면 값이 뒤집힌다

③ 파일·플래시 저장 저장한 기기와 읽는 기기가 다르면 깨진다. 부트로더가 펌웨어 헤더를 읽을 때 엔디안이 어긋나 크기를 잘못 읽는 사고가 있다.

④ 하드웨어 레지스터 일부 주변장치(이더넷 MAC, SD 카드 등)는 내부적으로 빅 엔디안 데이터를 다룬다. 데이터시트를 확인해야 한다.

안전한 해결 — 바이트 단위 직렬화

// 빅 엔디안으로 명시적 인코딩
void put_u32_be(uint8_t *b, uint32_t v) {
    b[0] = (uint8_t)(v >> 24);
    b[1] = (uint8_t)(v >> 16);
    b[2] = (uint8_t)(v >>  8);
    b[3] = (uint8_t)(v      );
}

uint32_t get_u32_be(const uint8_t *b) {
    return ((uint32_t)b[0] << 24) | ((uint32_t)b[1] << 16)
         | ((uint32_t)b[2] <<  8) |  (uint32_t)b[3];
}

시프트 연산은 값에 대한 것이지 메모리 배치에 대한 것이 아니다. v >> 24는 어느 엔디안에서든 최상위 바이트를 준다. 그래서 이 코드는 어떤 플랫폼에서 컴파일해도 같은 바이트열을 만든다.

이것이 엔디안 문제의 정석 해법이다 — 구조체를 그대로 보내지 말고 바이트열로 조립한다.

비트 엔디안은 또 다른 문제

바이트 안에서 비트 번호를 어느 쪽부터 세는지도 규약마다 다르다. CAN·이더넷 프레임 명세를 읽을 때 헷갈리는 지점이다. 비트 필드 구조체를 쓰면 안 되는 이유이기도 하다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — C 언어 핵심 — 포인터·메모리클래스·const/volatile·비트·struct 패딩