개발하는 두더지

리틀엔디안(Little Endian)과 빅 엔디안(Big Endian) 본문

Java,Android

리틀엔디안(Little Endian)과 빅 엔디안(Big Endian)

덜지 2018. 8. 23. 18:40

CPU가 메모리에 데이터를 저장할 때 어느 순서로 저장하는가에 따라서 리틀엔디안과 빅엔디안으로 나뉘게 됩니다.

리틀엔디안(Little Endian)은 메모리의 첫 주소에 하위 데이터(데이터의 맨 오른쪽)부터 저장하고 빅 엔디안(Big Endian)은 메모리의 첫 주소에 상위 데이터(데이터의 맨 왼쪽)부터 저장합니다.


예를들어  int a = 0x12345678 을 저장한다고 할 때 아래와 같이 저장됩니다.

 타입

메모리  ( int는 4byte 이므로 1byte씩 저장 )

 리틀엔디안

0x78 0x56 0x34 0x12 

 빅엔디안

0x12 0x34 0x56 0x78


리틀엔디안의 경우 저장할 때 뒤집혀서 저장했기때문에 가져올 때는 다시 되돌려서 가져옵니다.

- 1byte를 가져오는 경우 0x78

- 2byte를 가져오는 경우 0x5678

- 4byte를 가져오는 경우 0x12345678


만약 서로 다른 플랫폼끼리 네트워크 데이터 전송을 할 때 엔디안 방식에 주의를 해야하며 네트워크 바이트 오더(Network Byte Order)를 빅엔디안에 맞춰서 전송해야 합니다.  ( TCP/IP 규약을 보면 16비트 32비트 정수에서 빅 엔디안 방식을 사용한다고 쓰여있습니다. )


CPU 아키텍쳐에 따라 다르지만 일반적으로 Intel x86, x64, AMD 계열은 리틀엔디안을 사용하며 모토로라 프로세서들은 빅엔디안을 사용하고 ARM 프로세서들은 성능 향상을 위해 빅 엔디안과 리틀엔디안을 선택할 수 있습니다. 즉 퀄컴 같은 제조사가 엔디안을 선택하여 제조하므로 제조사의 설명서를 보면 어떤 엔디안을 사용했는지 알 수 있습니다.


자바는 CPU 아키텍처와 무관하게 JVM이라는 자바 바이트코드를 해석하고 실행하는 가상머신을 사용합니다. 즉 인텔 x86 아키텍처나 ARM 아키텍처와 같은 하드웨어가 레지스터 기반으로 동작하는 반면 JVM은 스택 기반으로 동작합니다. 그리고 플랫폼 독립성을 유지하기 위해 고정된 바이트 오더를 유지해야 하므로 네트워크 전송 시에 사용되는 바이트 오더인 네트워크 바이트 오더(빅 엔디안)를 사용합니다.

그래서 자바에서 문자열은 항상 UTF-16 BE로 저장하고, 문자열을 입/출력할 때에만 OS의 기본 인코딩 값 또는 사용자가 지정한 인코딩 값으로 문자열을 인코딩하여 출력합니다. 


안드로이드는 JVM을 쓰지않고 달빅(Dalvik) 가상 머신을 사용합니다. 달빅 가상 머신은 스택기반인 JVM과 다르게 레지스터 기반으로 동작하며 리틀 엔디안을 사용합니다. 하지만 안드로이드 내에서 네트워크 통신을 위해 ByteBuffer를 이용하여 Byte 배열을 만들어낸다면 빅 엔디언으로 저장하게 됩니다.

ByteOrder byteOrder = ByteOrder.nativeOrder();
Log.d("TEST", "ByteOrder is " + byteOrder.toString());

ByteOrder is LITTLE_ENDIAN 이라고 출력됩니다.

내부적으로는 리틀 엔디안으로 저장되고 문자열의 경우 직렬화, I/O, 출력, JNI로 전송할 때 기본 UTF-8로 인코딩하여 전송합니다.


그리고 ARM/X86/MIPS 계열 디바이스에서 NDK toolchain 은 리틀엔디안(Little Endian, LE )을 사용합니다.

NDK를 이용해 JNI를 구현하는 경우 2가지 메소드를 이용해 char 또는 jchar ( unsigned short, uint16_t, 2byte) 타입으로 변경할 수 있습니다.

이때 jchar의 경우 리틀엔디안으로 저장됩니다.

(char*) env->GetStringUTFChars(strPath, &bIsCopy);
(jchar*) env->GetStringChars(strUserId, &bIsCopy);


엔디안 과는 무관하지만 

C/C++에서 WCHAR(wchar_t) 인 유니코드 캐릭터형이 있습니다.  VS 기준으로 2바이트의 크기입니다.

JNI에도 whar_t 타입이 있는데 크기가 4바이트입니다.  안드로이드 2.1 이하에서는 2바이트였는데 이후 버전부터 4바이트로 변경되었습니다.

타입의 크기를 정확하게 확인하고 사용해야 합니다. 

uint16_t / unsigned short 를 이용하여 메모리를 카피하거나 wchar_t에서 상위 2바이트를 버리고 하위 2바이트를 저장하는 방법으로 사용해야 합니다.





출처 

https://en.wikipedia.org/wiki/Endianness

https://www.quora.com/Is-ARM-big-endian-or-little-endian

디언 위키백과

JVM 상세 설명

https://groups.google.com/forum/#!topic/comp.lang.java.machine/MubhIckREf4

안드로이드와 자바와의 비교

https://stackoverflow.com/questions/21741826/which-is-the-most-efficient-getbytes-in-android-to-get-a-utf-16-byte-array

Comments