개요
이 글에서는 서로 다른 아키텍처에서 바이트를 주고 받을 때 신경 써야 하는 바이트 배열 방법, 엔디안에 대해서 알아볼 것이다.
엔디안
엔디안(Endian)은 1차원의 공간에 여러 개의 연속된 대상을 배열하는 방법을 뜻한다.
특히 컴퓨터에서 바이트를 메모리에 나열할 때 두 가지 방법이 있다.
하나는 빅 엔디안(Big endian)이고 하나는 리틀 엔디안(Little endian)이다.
빅 엔디안과 리틀 엔디안
빅 엔디안은 우리가 바이트 숫자를 쓸 때와 똑같다.
최상위 비트(Most Significant Bit, MSB)를 메모리의 가장 낮은 주소에 배치시켜 나머지를 쭉 나열시킨다.
말그대로 제일 큰 값을 먼저 나열시키는 방법이다.
사람이 숫자를 읽고 쓰는 방법과 똑같기 때문에 디버깅 과정에서 메모리의 값을 보기 편하다는 장점이 있다.
리틀 엔디안은 빅 엔디안과 반대로 생각하면 된다.
최하위 비트(Least Significant Bit, LSB)를 메모리의 가장 낮은 주소에 배치시켜 나머지를 쭉 나열시킨다.
말그대로 가장 작은 값을 먼저 나열시키는 방법이다.
보기에는 불편하지만, 수치 계산시나 하위 비트만 취할 경우에는 빠르다는 장점이 있다.
예를 들어 우리가 덧셈을 할 때, 낮은 비트부터 더해주기 시작해 자리 올림(carry)처리를 하면서 다음 비트를 계산한다. 리틀 엔디안은 낮은 비트부터 나열시키기 때문에 차례로 읽어가면서 계산해주면 된다.
하위 비트를 읽을 때 리틀 엔디안은 메모리의 낮은 주소를 바로 읽으면 되지만, 빅 엔디안은 바이트 오프셋을 더해줘 읽어야 하는 점이 있다.
중요성
두 방법 중 어느 한 쪽이 압도적으로 좋거나 나쁘지는 않다.
x86아키텍처는 리틀 엔디안을 사용하며, 네트워크에서는 주소를 빅 엔디안으로 사용하는데 이의 영향으로 많은 프로토콜과 파일 포맷이 빅 엔디안을 사용하고 있다. 이처럼 서로 다른 아키텍처에서 공존하고 있기 때문에 이 포맷을 맞춰주는게 중요하다. 예를 들어 한국 만화책을 읽을 때 오른쪽으로 넘기는 것과 일본 만화책을 읽을 때 왼쪽으로 넘기는 것을 생각하면 된다. 데이터를 읽을 땐 형식에 맞춰서 읽어야 한다.
보통 하드웨어 또는 프로토콜에 이미 처리해주는 식으로 추상화되어있기 때문에 직접 접할 일은 많지 않다.
하지만 서로 다른 아키텍처에서 바이트를 주고 받을 때, 특히 네트워크로 데이터를 송수신할 때는 이와 관련해서 문제를 겪을 가능성이 꽤 높다.
C# BitConverter 클래스의 예제에는 다음과 같이 나와있다.
시스템에서 사용하는 엔디안 방식에 따라 뒤집어주는 에제이다.
using System;
public class Example
{
public static void Main()
{
int value = 12345678;
byte[] bytes = BitConverter.GetBytes(value);
Console.WriteLine(BitConverter.ToString(bytes));
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
Console.WriteLine(BitConverter.ToString(bytes));
// Call method to send byte stream across machine boundaries.
// Receive byte stream from beyond machine boundaries.
Console.WriteLine(BitConverter.ToString(bytes));
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
Console.WriteLine(BitConverter.ToString(bytes));
int result = BitConverter.ToInt32(bytes, 0);
Console.WriteLine("Original value: {0}", value);
Console.WriteLine("Returned value: {0}", result);
}
}
// The example displays the following output on a little-endian system:
// 4E-61-BC-00
// 00-BC-61-4E
// 00-BC-61-4E
// 4E-61-BC-00
// Original value: 12345678
// Returned value: 12345678
결론
이렇게 해서 빅 엔디안과 리틀 엔디안에 대해서 알아보았다.
빅 엔디안은 최상위 비트를 메모리 낮은 주소부터, 리틀 엔디안은 최하위 비트를 메모리 낮은 주소부터 나열 시킨다.
네트워크 상에서 바이트를 주고받는 프로그램을 작성한다면 거의 필수적으로 처리를 해줘야 하기 때문에 꼭 알아놔야 한다. 우리가 사용하는 x86 아키텍처는 주로 리틀 엔디안을 사용하지만 네트워크 통신은 주로 빅 엔디안 방식을 사용한다.