오늘은 좀 뜬금없지만 엔디언에 대해서 잠시 포스팅을 하려고 합니다.

흠 먼저 엔디안이란 무엇이냐! 바로 연속된 숫자를 배열하는 방법인데요. 크게 빅 엔디안과 리틀 엔디안이 있습니다. 위키피디아 정의를 잠깐 보도록 하죠.

엔디언(Endianness)은 컴퓨터의 메모리와 같은 1차원의 공간에 여러 개의 연속된 대사을 배열하는 방법을 뜻하며, 바이트를 배열하는 방법을 특히 바이트 순서(Byte order)라 한다.

엔디언은 보통 큰 단위가 앞에 나오는 빅 엔디언(Big-endian)과 작은 단위가 앞에 나오는 리틀 엔디언(Little-endian)으로 나눌 수 있으며, 두 경우에 속하지 않거나 둘을 모두 지원하는 것을 미들 엔디언(Middle-endian)이라 부르기도 한다.


출처: 위키피디아 엔디언

그림을 보니 이해가 되죠? 솔직히 우리가 일반적으로 쓰는 컴퓨터는 대부분 리틀 엔디언이랍니다. 그런데 이런 리틀 엔디언과 빅 엔디언이 어떤 차이가 있을까요? 먼저 빅 엔디언은 사람이 숫자를 읽고 쓰는 방법과 같기 때문에 디버깅과정에서 사람이 메모리의 값을 이해하기가 더 편하답니다. 반면에 리틀 엔디언은 메모리에 저장된 하위 바이트들을 컴퓨터가 떼어 내서 쓸때 별도의 연산과정이 없기 때문에 컴퓨터가 연산에 이점이 있죠. 다음 그림을 보면서 마저 이야기를 해 봅시다.

int형 포인터와 char포인터, short포인터를 이용해서 각각 같은 주소를 가리키게 만든 다음, intPtr[0]에 -20을 저장하였다고 가정하여 봅시다. 컴퓨터는 -값을 다룰때 보통 2의 보수로 저장하기 때문에 -20은 11111111 11111111 11111111 11101100 으로 변환하여 메모리에 저장하게 됩니다. 여기서 리틀 엔디안과 빅엔디안이 저장되는 방식을 표에서 한번 보죠. 리틀 엔디안은 바이트를 거꾸로 정렬해서 메모리에 집어넣고 빅엔디안은 우리가 보는 그대로 메모리에 집어넣죠? 그래서 빅 엔디안은 우리가 디버깅하며 메모리 값을 보기에 편한겁니다. 그러나 만약에 우리가 short변수로 -20을 떼어낸 다고 생각해 봅시다. 그렇다면 빅 엔디안은 shortPtr[1]을 떼어내야 -20이 정상적으로 출력 될테고, 리틀 엔디안은 그냥 short[10]를 떼어내야겠죠? 즉 리틀 엔디안은 주소의 시작값부터 스택에 넣어 읽어들이면 되지만 빅 엔디안은 저장되어 있는 범위의 맨 뒤에서부터 역방향으로 읽어야 한다는 이야기 입니다. 각각 장점과 단점이 존재하죠. 근데 사실 오늘날의 프로세서는 여러개의 바이트를 동시에 읽어들여 동시에 계산을 수행하기 때문에 두 엔디언 사이에 사실상 차이가 없다는 군요. 바이엔디언과 미들 엔디언이 또 있는데 간단하게 요약만 하고 넘어가도록 하겠습니다.

바이 엔디언: 빅 엔디언과 리틀 엔디언 둘 중 하나를 선택 할 수 있는 것.

미들 엔디언: 빅 엔디언과 리틀 엔디언 둘 다 사용하는 것.(예를 들면 32비트 정수가 2바이트 단위로는 빅 엔디언이고 그 안에서 1바이트 단위로는 리틀 엔디언인 경우)

다음은 이를 확인해 보는 c++코드 입니다. 여러분의 컴퓨터가 리틀 엔디언이라고 가정하였습니다. 만약에 두번째 줄이 위 그림의 빅엔디언 처럼 나오면 여러분의 컴퓨터는 빅 엔디언인 거겠죠^^

#include <iostream>
#include <bitset>
using namespace std;

int main() {
	int *intPtr = new int [1];
	short *shortPtr;
	char *charPtr;
	*intPtr = -20;
	shortPtr = (short*)intPtr;
	charPtr = (char*)intPtr;
	cout << "-20을 2의 보수로 표현: ";
	for (int i = 3; i >= 0; i--) {
		bitset<8> tempBit;
		tempBit = charPtr[i];
		cout << tempBit << " ";
	}
	cout << endl << "저장되어 있는 비트\t";
	for (int i = 0; i < sizeof(int); i++) {
		bitset<8> tempBit;
		tempBit = charPtr[i];
		cout << i << ":" <<tempBit << "\t";
	}
	cout << endl;
	cout << "int로 4byte를 읽었을때: "<<*intPtr<< endl;

	cout <<"shortPtr[0]: ";
	bitset<8> bitSet0(charPtr[0]);
	cout << bitSet0 << "\t";
	bitset<8> bitSet1(charPtr[1]);
	cout <<bitSet1 << "\t";
	cout << "shortPtr[1]: ";
	bitset<8> bitSet2(charPtr[2]);
	cout << bitSet2 << "\t";
	bitset<8> bitSet3(charPtr[3]);
	cout << bitSet3 << "\t";

	cout << endl << "short로 2byte씩 읽었을때: " << endl;
	for (int i = 0; i < sizeof(int) / sizeof(short); i++) {
		cout << i << "번째: " << shortPtr[i] << "\t";
	}
	cout << endl;

	return 0;
}


+ Recent posts