by 꽈배기
엔디언(Endianness)은 컴퓨터 시스템에서 멀티바이트 데이터를 메모리에 저장하거나 전송할 때 바이트의 순서를 정의하는 방식입니다. 주로 두 가지 방식이 있습니다: 리틀 엔디언(Little Endian)과 빅 엔디언(Big Endian).
리틀 엔디언 방식에서는 데이터의 하위 바이트(Least Significant Byte, LSB)가 메모리의 낮은 주소에 저장됩니다. 즉, 숫자의 가장 작은 단위가 먼저 저장됩니다. 이 방식은 인텔 x86 계열의 프로세서에서 주로 사용됩니다.
예를 들어, 4바이트 정수 0x12345678을 리틀 엔디언 방식으로 메모리에 저장하면 다음과 같이 저장됩니다:
주소: 0x00 0x01 0x02 0x03
값: 0x78 0x56 0x34 0x12
빅 엔디언 방식에서는 데이터의 상위 바이트(Most Significant Byte, MSB)가 메모리의 낮은 주소에 저장됩니다. 즉, 숫자의 가장 큰 단위가 먼저 저장됩니다. 이 방식은 네트워크 프로토콜과 일부 RISC 프로세서에서 주로 사용됩니다.
예를 들어, 4바이트 정수 0x12345678을 빅 엔디언 방식으로 메모리에 저장하면 다음과 같이 저장됩니다:
주소: 0x00 0x01 0x02 0x03
값: 0x12 0x34 0x56 0x78
다음은 C++로 리틀 엔디언과 빅 엔디언을 확인하고 변환하는 예제입니다.
#include
#include
// 엔디언을 확인하는 함수
bool isLittleEndian() {
uint16_t number = 0x1;
uint8_t *bytePtr = reinterpret_cast<uint8_t*>(&number);
return (bytePtr[0] == 0x1);
}
// 엔디언 변환 함수 (리틀 엔디언 <-> 빅 엔디언)
uint32_t swapEndian(uint32_t num) {
return ((num >> 24) & 0x000000FF) |
((num >> 8) & 0x0000FF00) |
((num << 8) & 0x00FF0000) |
((num << 24) & 0xFF000000);
}
int main() {
uint32_t num = 0x12345678;
std::cout << "Original number: 0x" << std::hex << num << std::endl;
if (isLittleEndian()) {
std::cout << "System is Little Endian" << std::endl;
} else {
std::cout << "System is Big Endian" << std::endl;
}
uint32_t swappedNum = swapEndian(num);
std::cout << "Swapped number: 0x" << std::hex << swappedNum << std::endl;
return 0;
}
isLittleEndian
함수는 시스템이 리틀 엔디언인지 빅 엔디언인지 확인합니다. 16비트 정수 0x1
를 바이트 포인터로 변환하여 첫 번째 바이트가 0x1
인지 확인합니다. 만약 그렇다면, 시스템은 리틀 엔디언입니다.swapEndian
함수는 32비트 정수의 바이트 순서를 바꿉니다. 비트 연산을 통해 각 바이트를 적절한 위치로 이동시킵니다.main
함수에서는 원래 숫자를 출력하고, 시스템의 엔디언 타입을 확인한 후, 바이트 순서를 바꾼 숫자를 출력합니다.좋다 그렇다면 LittleEndian이 메모리 관점에서 어떻게 되는지 보자면 아래와 같이 저장된다.
저장된 num의 메모리 주소에 접근해보면, 메모리 num 기준 0x00에는 78, 0x01에는 56 순서대로 가장 낮은 숫자의 단위가 가장 먼저 저장되는 것을 볼 수 있다.
그 다음 swapEndian의 경우 아래와 같은 로직으로 진행된다.
주어진 함수 swapEndian
는 32비트 정수의 바이트 순서를 바꾸는 역할을 합니다.
uint32_t swapEndian(uint32_t num) {
return ((num >> 24) & 0x000000FF) |
((num >> 8) & 0x0000FF00) |
((num << 8) & 0x00FF0000) |
((num << 24) & 0xFF000000);
}
모든 과정은 반복되는데, 비트를 쉬프트 한 결과에 비트 & 연산을 수행함으로 겹치는 부분만 추출하는 방식이다.
(num >> 24) & 0x000000FF
num
을 (2진수 기준 2^4^, 32 - FF 이므로) 24비트 오른쪽으로 이동시킵니다. 이렇게 하면 원래 num
의 가장 왼쪽 바이트 가 가장 오른쪽 바이트로 이동합니다.& 0x 00 00 00 FF
는 상위 3바이트를 0으로 만들고, 하위 1바이트만 남깁니다.
예를 들어, num
이 0x12345678
이라면, num >> 24
는 0x00000012
가 되고, & 0x000000FF
를 적용하면 여전히 0x00000012
가 됩니다.
(num >> 8) & 0x0000FF00
num
을 8비트 오른쪽으로 이동시킵니다. 이렇게 하면 원래 num
의 두 번째 바이트가 가장 오른쪽 바이트로 이동합니다.& 0x0000FF00
는 상위 2바이트와 하위 1바이트를 0으로 만들고, 두 번째 바이트만 남깁니다.
예를 들어, num
이 0x12345678
이라면, num >> 8
은 0x00123456
가 되고, & 0x0000FF00
를 적용하면 0x00003400
가 됩니다.
(num << 8) & 0x00FF0000
num
을 8비트 왼쪽으로 이동시킵니다. 이렇게 하면 원래 num
의 세 번째 바이트가 두 번째 바이트로 이동합니다.& 0x00FF0000
는 상위 1바이트와 하위 2바이트를 0으로 만들고, 세 번째 바이트만 남깁니다.num
이 0x12345678
이라면, num << 8
은 0x34567800
가 되고, & 0x00FF0000
를 적용하면 0x00560000
가 됩니다. (num << 24) & 0xFF000000
num
을 24비트 왼쪽으로 이동시킵니다. 이렇게 하면 원래 num
의 네 번째 바이트가 가장 왼쪽 바이트로 이동합니다.& 0xFF000000
는 하위 3바이트를 0으로 만들고, 네 번째 바이트만 남깁니다.num
이 0x12345678
이라면, num << 24
는 0x78000000
가 되고, & 0xFF000000
를 적용하면 여전히 0x78000000
가 됩니다. return ((num >> 24) & 0x000000FF) |
((num >> 8) & 0x0000FF00) |
((num << 8) & 0x00FF0000) |
((num << 24) & 0xFF000000);
|
)을 사용하여 합칩니다.num
이 0x12345678
이라면, 각 단계의 결과는 다음과 같습니다:0x00000012
0x00003400
0x00560000
0x78000000
0x78563412
가 됩니다.이 함수는 주어진 32비트 정수의 바이트 순서를 반대로 바꾸어 리틀 엔디안에서 빅 엔디안으로, 또는 그 반대로 변환합니다.
음 그렇다. 엔디안 변환법은 비트 시프트와 &연산으로 필요한 부분만 마스킹하고 나머지 결과들을 모두 | 연산으로 합치는것이다. |
리틀 엔디안 방식이 물리적으로 데이터를 조작하거나 산술 연산을 수행할 때 더 효율적인 이유는 다음과 같습니다
빅 엔디안 방식이 데이터의 각 바이트를 배열처럼 취급할 때 더 적합한 이유는 다음과 같습니다
운영체제의 비트 수와 엔디안 방식은 직접적인 연관이 없습니다. 엔디안 방식은 주로 CPU 아키텍처에 의해 결정됩니다. 그러나 특정 운영체제는 특정 엔디안 방식을 더 선호할 수 있습니다.
32비트 운영체제: 대부분의 32비트 x86 아키텍처는 리틀 엔디안 방식을 사용합니다. 이는 인텔과 AMD의 x86 프로세서가 리틀 엔디안 방식을 채택했기 때문입니다.
64비트 운영체제: 64비트 x86-64 아키텍처도 리틀 엔디안 방식을 사용합니다. 그러나 일부 64비트 아키텍처, 예를 들어 IBM의 PowerPC나 SPARC 아키텍처는 빅 엔디안 방식을 사용하거나, 둘 다 지원하는 경우도 있습니다.
결론적으로, 엔디안 방식은 CPU 아키텍처와 밀접한 관련이 있으며, 특정 상황에서의 효율성은 하드웨어 설계와 데이터 처리 방식에 따라 달라집니다.
tags: ComputerScience