Byte Order

Development/C/C++ 2011. 8. 13. 15:04

출처 : http://superkkt.com/138

 

오늘은 바이트 오더에 대해서 정리를 해보자. 책을 찾아보면 이해가 되었다가 한참후에 생각해보면 항상 햇갈리는 부분이라서 따로 정리를 해본다.

바이트 오더에는 리틀엔디안과 빅엔디안 두가지가 있다. PC에서 일반적으로 사용하는 Intel, Alpha 같은 CISC 계열 CPU는 리틀엔디안을 사용하고 SPARC, 모토로라 프로세서 같은 RISC 계열 CPU는 빅엔디안을 사용한다.

그럼 두가지 엔디안의 차이는 무엇일까? 차이점은 2바이트 이상의 데이터(정수형뿐만 아니라 일반대상체도 포함됨)를 메모리에 저장할때 어느 바이트부터 저장을 하는지이다. 리틀엔디안은 높은 번지(Most Significant Byte)에서 낮은 번지(Least Siginificant Byte)로 기록을 하고, 빅엔디안은 낮은 번지(LSB)에서 높은 번지(MSB)로 기록을 한다.

컴퓨터는 2진수를 사용해서 데이터를 메모리에 저장한다. C언어에서 사용하는 정수형(int)은 4바이트를 사용하는데 정수형 134480385 값은 아래와 같이 표현된다. 오른쪽 끝이 2^0 부분이고 왼쪽으로 갈수록 증가해서 왼쪽 끝이 2^31 이다. (2진수 값을 보기 편하게 하기위해 볼드 처리를 하고 134480385 같은 이상한 숫자를 사용했다)

00001000 00000100 00000010 00000001


위의 표현이 빅엔디안 시스템에서 메모리에 저장될때는 낮은 번지(LSB)에서부터 높은 번지(MSB)로 기록을 하기 때문에 아래와 같이 변경없이 그대로 저장이 된다.

LSB <-- 00001000 00000100 00000010 00000001  --> MSB


하지만 리틀엔디안 시스템은 높은 번지(MSB)에서부터 낮은 번지(LSB)로 저장을 하기 때문에 아래와 같이 각 바이트의 순서가 뒤집어져서 저장이 된다. 오른쪽에서부터 왼쪽으로 데이터를 저장한다고 생각하면 이해하기 쉬울것이다.

LSB <--  00000001 00000010 00000100 00001000  --> MSB


!주의!
바이트를 구성하는 비트는 엔디안의 영향을 받지 않는다. 즉, 위에서 설명한대로 리틀엔디안 시스템에서 순서가 뒤집혀서 저장되는것은 2진수로 변환된 전체값을 반대로 뒤집는것이 아니라, 바이트 단위로 끊어서 뒤집어 저장하는 것이다.


그럼 두 엔디안의 차이에서 발생하는 문제가 무엇일까?

위에서 보여준 리틀 엔디안으로 저장된 134480385의 2진수 표현을 빅엔디안 방식으로 해석해보자. 빅엔디안은 낮은 번지부터 시작한다고 했으니 왼쪽에서 오른쪽으로 읽어나가면 되겠다. 그럼 1로 셋팅된 값이 각각 2^24, 2^17, 2^10, 2^3 자리에 있으니 10진수로 변환하면 16909320이 된다. 졸지에 134480385가 16909320로 변했다!!


두가지 방법에는 나름대로 장단점이 있다.

빅엔디안의 경우에는 사림이 보기에 매우 직관적이라는 장점이 있지만, 데이터 타입이 변경되어 4바이트에서 8바이트로 확장될때 메모리에서 전체 4바이트 값을 오른쪽으로 이동시켜야한다.

LSB <-- 00001000 00000100 00000010 00000001 --> MSB

LSB <-- 00000000 00000000 00000000 00000000 00001000 00000100 00000010 00000001 --> MSB


리틀엔디안은 사람이 보기에는 별로 직관적이지 않지만 데이터 타입 변경으로 메모리가 확장될때 단지 오른쪽에 새로운 바이트를 추가하기만 하면 된다.

LSB <-- 00000001 00000010 00000100 00001000 --> MSB

LSB <-- 00000001 00000010 00000100 00001000 00000000 00000000 00000000 00000000 --> MSB



그럼 이제 언제 엔디안의 차이가 문제를 발생시키는지 알아보자. 빅엔디안이던 리틀엔디안이던간에 그 컴퓨터에서 뭔짓을 하더라도 아무런 문제도 생기지 않는다. 하지만 요즘 세상에 혼자만 노는 컴퓨터가 있던가? 다들 다른 컴퓨터와 네트웍으로 연결되어서 통신을 한다. 바로 이때 문제가 발생한다. 리틀엔디안 컴퓨터가 512라는 숫자를 전송했는데 받는 컴퓨터는 빅엔디안이다. 그럼 빅엔디안 컴퓨터는 그 값을 131072로 해석하겠지.

이런 문제를 해결하기 위한 방법으로 네트웍 오더라는 개념이 있다. 모든 컴퓨터는 네트웍으로 정수형을 전달하기 전에 네트웍 오더로 변환 후에 전송해야 한다. 그리고 받는쪽은 네트웍 오더를 자신이 사용하는 바이트 오더로 변환 후에 그 값을 사용해야만 한다. C에서는 htons, htonl, ntohs, ntohl 같은 함수를 사용해서 변환을 한다. 참고로 네트웍 오더는 빅 엔디안이다. 그래서  솔라리스 스팍 버전에서 htons, htonl 함수는 비어있는 메크로이다.

바이트오더 문제를 해결하는 또 다른 방법은 숫자를 문자열로 전송하는 방법이다. 512라는 값을 전송할때 정수형 512로 전송하는게 아니라 문자열 512로 전송한다. 그리고 받는 쪽에서 문자열 512를 정수형으로 변환해서 사용하면 된다. 이 방법은 사용하기 편하다는 장점이 있지만 숫자를 표현하는데 정수형보다 더 많은 바이트를 사용해야하고 숫자 표현에 몇 바이트를 사용할지 송수신자 사이에 약속이 있어야 한다. 몇 바이트를 사용할지 고정하지 않는 경우에는 구분자를 사용해서 어디까지가 숫자를 표현하는 문자열인지 표시해야 한다.

'Development > C/C++' 카테고리의 다른 글

cast 연산자  (0) 2011.08.13
c++ 추상 기초클래스  (0) 2011.08.13
ASCII Code Table  (0) 2011.08.13
ACE 란 무엇인가?  (0) 2011.08.13
프로젝트에서 Console창을 띄우기  (0) 2011.08.13
안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,