Little Endian vs. Big Endian

Β· 1575 words Β· 4 minute read

κ°œμš” πŸ”—

λΉ… μ—”λ””μ•ˆκ³Ό 리틀 μ—”λ””μ•ˆμ— κ΄€ν•΄ μ—…λ¬΄μ—μ„œ μ ‘ν•  수 μžˆλŠ” 상황과 각각에 ν•΄λ‹Ήν•˜λŠ” 포인터 μ—°μ‚° 예제λ₯Ό μ’…ν•©μ μœΌλ‘œ μ •λ¦¬ν•˜λ„λ‘ ν•œλ‹€.

Endian 비ꡐ

μ—”λ””μ•ˆ(Endianness)은 μ»΄ν“¨ν„°μ˜ λ©”λͺ¨λ¦¬μ™€ 같은 1차원 곡간에 μ—¬λŸ¬ 개의 μ—°μ†λœ λŒ€μƒμ„ λ°°μ—΄ν•˜λŠ” 방법을 λœ»ν•˜λ©°, λ°”μ΄νŠΈλ₯Ό λ°°μ—΄ν•˜λŠ” 방법을 λ°”μ΄νŠΈ μˆœμ„œ(Byte-order)라고 ν•œλ‹€. μ—”λ””μ•ˆμ€ 보톡 큰 λ‹¨μœ„κ°€ μ•žμ— λ‚˜μ˜€λŠ” λΉ… μ—”λ””μ•ˆ(Big-Endian)κ³Ό μž‘μ€ λ‹¨μœ„κ°€ μ•žμ— λ‚˜μ˜€λŠ” 리틀 μ—”λ””μ•ˆ(Little-Endian)으둜 λ‚˜λˆŒ 수 있으며, 두 κ²½μš°μ— μ†ν•˜μ§€ μ•Šκ±°λ‚˜ λ‘˜ λͺ¨λ‘ μ§€μ›ν•˜λŠ” 것을 λ―Έλ“€ μ—”λ””μ•ˆ(Middle-Endian)이라 λΆ€λ₯Έλ‹€.

  • Big-Endian: μ΅œμƒμœ„ λ°”μ΄νŠΈ(MSB)λΆ€ν„° μ°¨λ‘€λ‘œ μ €μž₯ν•˜λŠ” 방식 (μ‚¬λžŒμ΄ 읽고 μ“°λŠ” 방식과 λΉ„μŠ·ν•¨)
  • Little-Endian: μ΅œν•˜μœ„ λ°”μ΄νŠΈ(LSB)λΆ€ν„° μ°¨λ‘€λ‘œ μ €μž₯ν•˜λŠ” 방식

예제 πŸ”—

예λ₯Ό λ“€μ–΄, λ©”λͺ¨λ¦¬μ— 0x12345678을 λŒ€μž…ν•œλ‹€κ³  ν–ˆμ„ λ•Œ, λΉ… μ—”λ””μ•ˆκ³Ό 리틀 μ—”λ””μ•ˆ 각각 μ•„λž˜μ™€ 같이 μ €μž₯λœλ‹€.

Byte order in memory

λΉ… μ—”λ””μ•ˆμ€ μ‚¬λžŒμ΄ 숫자λ₯Ό μ‚¬μš©ν•˜λŠ” 것과 같이 큰 λ‹¨μœ„μ˜ λ°”μ΄νŠΈκ°€ μ•žμ— μ˜€λŠ” 방법이고 리틀 μ—”λ””μ•ˆμ€ λ°˜λŒ€λ‘œ μž‘μ€ λ‹¨μœ„μ˜ λ°”μ΄νŠΈκ°€ μ•žμ— μ˜€λŠ” 방법이닀.

#include <stdio.h>

int main(void) {
	unsigned long value = 0x12345678;
	unsigned char* ptr = &value;

	int i;

	for (i = 0; i < 4; i++) {
		fprintf(stdout, "value[%d] = 0x%x\n", i, *ptr++);
	}

	unsigned long long value2 = 0x12345678abcdefab;
	ptr = &value2;
	for (i = 0; i < sizeof(value2); i++) {
		fprintf(stdout, "value2[%d] = 0x%x\n", i, *ptr++);
	}

	return 0;
}

μœ„μ˜ μ½”λ“œλ₯Ό μ»΄νŒŒμΌν•˜μ—¬ Mac OS ν™˜κ²½μ—μ„œ μ‹€ν–‰ν•˜λ©΄ μ•„λž˜μ™€ 같은 κ²°κ³Όλ₯Ό 얻을 수 μžˆλ‹€.

~/Workspaces/study/languages/modernc/endian $ ./endian
value[0] = 0x78
value[1] = 0x56
value[2] = 0x34
value[3] = 0x12
value2[0] = 0xab
value2[1] = 0xef
value2[2] = 0xcd
value2[3] = 0xab
value2[4] = 0x78
value2[5] = 0x56
value2[6] = 0x34
value2[7] = 0x12

0x12345678μ—μ„œ MSBλŠ” 0x12, LSBλŠ” 0x78이며, LSBκ°€ 처음 λ‚˜μ˜€λŠ” κ²ƒμœΌλ‘œ 보아 리틀 μ—”λ””μ•ˆ λ°©μ‹μœΌλ‘œ Byte-ordering을 ν•˜κ³  μžˆλŠ” 것을 μ•Œ 수 μžˆλ‹€. unsigned long long의 경우둜 확인할 수 μžˆλ“―μ΄ 4λ°”μ΄νŠΈλ‚˜ 8λ°”μ΄νŠΈ λ‹¨μœ„λ‘œ byte-ordering λ˜λŠ” 것이 μ•„λ‹ˆλΌ ν•΄λ‹Ή 데이터 νƒ€μž…μ— 따라 λ‹¬λΌμ§€λŠ” 것을 μ•Œ 수 μžˆλ‹€.

μž₯/단점 πŸ”—

가독성 πŸ”—

Big-Endian은 μ†Œν”„νŠΈμ›¨μ–΄μ˜ 디버그λ₯Ό νŽΈν•˜κ²Œ ν•΄μ£ΌλŠ” κ²½ν–₯이 μžˆλ‹€. μ‚¬λžŒμ΄ 숫자λ₯Ό 읽고 μ“°λŠ” 방법과 κ°™κΈ° λ•Œλ¬Έμ— 디버깅 κ³Όμ •μ—μ„œ λ©”λͺ¨λ¦¬μ˜ 값을 보기 νŽΈν•˜λ‹€. 예λ₯Ό λ“€μ–΄, 0x59654148을 Big-Endian으둜 ν‘œν˜„ν•˜λ©΄ 0x59, 0x65, 0x41, 0x48 λ“±μœΌλ‘œ λ©”λͺ¨λ¦¬μ— μˆœμ„œλŒ€λ‘œ ν‘œν˜„λœλ‹€.

λ°˜λŒ€λ‘œ Little-Endian은 λ©”λͺ¨λ¦¬μ— μ €μž₯된 κ°’μ˜ ν•˜μœ„ λ°”μ΄νŠΈλ“€λ§Œ μ‚¬μš©ν•  λ•Œ λ³„λ„μ˜ 계산이 ν•„μš” μ—†λ‹€λŠ” μž₯점이 μžˆλ‹€. 예λ₯Ό λ“€μ–΄, 32λΉ„νŠΈ 숫자인 0x2AλŠ” 리틀 μ—”λ””μ–ΈμœΌλ‘œ ν‘œν˜„ν•˜λ©΄ 2A 00 00 00이 λ˜λŠ”λ°, 이 ν‘œν˜„μ—μ„œ μ•žμ˜ 두 λ°”μ΄νŠΈ λ˜λŠ” ν•œ λ°”μ΄νŠΈλ§Œ λ–Όμ–΄λ‚΄λ©΄ ν•˜μœ„ 16λΉ„νŠΈ λ˜λŠ” 8λΉ„νŠΈλ₯Ό λ°”λ‘œ 얻을 수 μžˆλ‹€. 반면 32λΉ„νŠΈ λΉ… μ—”λ””μ•ˆ ν™˜κ²½μ—μ„œλŠ” ν•˜μœ„ 16λΉ„νŠΈλ‚˜ 8λΉ„νŠΈ 값을 μ–»κΈ° μœ„ν•΄ λ³€μˆ˜ μ£Όμ†Œμ— 2λ°”μ΄νŠΈ λ˜λŠ” 3λ°”μ΄νŠΈλ₯Ό 더해야 ν•œλ‹€.

컀널 λ‚΄ μΈν„°νŽ˜μ΄μŠ€ πŸ”—

컀널은 byte order 에 λŒ€ν•œ μ˜μ‘΄μ„±μ„ ν•΄κ²°ν•˜κΈ° μœ„ν•΄ Type Identifier, Conversion Macro 등을 μ œκ³΅ν•˜κ³  μžˆλ‹€. include/uapi/linux/types.h 헀더 파일 λ‚΄μ—μ„œλŠ” μ•„λž˜μ™€ 같이 μ—”λ””μ•ˆ λ³„λ‘œ νƒ€μž…λ“€μ΄ μ •μ˜λ˜μ–΄ μžˆλŠ” 것을 μ•Œ 수 μžˆλ‹€. μ—¬κΈ°μ„œ uapi λ””λ ‰ν† λ¦¬λŠ” μ»€λ„μ˜ userspace APIλ₯Ό ν¬ν•¨ν•˜κ³  μžˆλ‹€. (μ°Έκ³ . https://stackoverflow.com/questions/18858190/whats-in-include-uapi-of-kernel-source-project)

νƒ€μž… μ •μ˜ πŸ”—

/*
 * Below are truly Linux-specific types that should never collide with
 * any application/library that wants linux/types.h.
 */

#ifdef __CHECKER__
#define __bitwise__ __attribute__((bitwise))
#else
#define __bitwise__
#endif
#define __bitwise __bitwise__

typedef __u16 __bitwise __le16;
typedef __u16 __bitwise __be16;
typedef __u32 __bitwise __le32;
typedef __u32 __bitwise __be32;
typedef __u64 __bitwise __le64;
typedef __u64 __bitwise __be64;

typedef __u16 __bitwise __sum16;
typedef __u32 __bitwise __wsum;

bitwise 속성(λ‹¨μˆœνžˆ μ •μˆ˜λ‘œμ¨ μ‚¬μš©λ˜λŠ” 것을 μ œν•œν•˜λŠ”λ° μ‚¬μš©)으둜 μ •μ˜λ˜μ–΄ μžˆλŠ” Type Identifiers 듀이닀. bitwise 속성은 sparse μœ ν‹Έλ¦¬ν‹°(static analyzer)κ°€ λ³€μˆ˜μ— λŒ€ν•œ 연산을 μˆ˜ν–‰ν•˜κΈ° 전에 둜컬 ν”„λ‘œμ„Έμ„œλ‘œ λ³€ν™˜λ  수 μžˆλ„λ‘ 보μž₯ν•œλ‹€.

Byte Order μ•Œμ•„λ‚΄κΈ° πŸ”—

μ•„λž˜μ™€ 같이 κ°„λ‹¨ν•œ user-space ν”„λ‘œκ·Έλž¨μ„ μž‘μ„±ν•˜μ—¬ ν˜„μž¬ μ‹œμŠ€ν…œμ˜ λ°”μ΄νŠΈ μ˜€λ”λ₯Ό μ•Œμ•„λ‚Ό 수 μžˆλ‹€.

union {
    int i;
    char c[sizeof(int)];
} foo;

main() {
    foo.i = 1;
    if (foo.c[0] == 1)
        printf("Little endian\n");
    else
        printf("Big endian\n");
}

λ‹€μŒμ— μ†Œκ°œλ˜λŠ” λ§€ν¬λ‘œλŠ” λ³€ν™˜ ν›„μ˜ 값듀을 λ°˜ν™˜ν•œλ‹€.

#include <linux/kernel.h>

__u16	le16_to_cpu(const __le16);
__u32	le32_to_cpu(const __le32);
__u64	le64_to_cpu(const __le64);

__le16	cpu_to_le16(const __u16);
__le32	cpu_to_le32(const __u32);
__le64	cpu_to_le64(const __u64);

__u16	be16_to_cpu(const __be16);
__u32	be32_to_cpu(const __be32);
__u64	be64_to_cpu(const __be64);

__be16	cpu_to_be16(const __u16);
__be32	cpu_to_be32(const __u32);
__be64	cpu_to_be64(const __u64);

포인터에 λŒ€ν•œ λ³€ν™˜μ€ pλ₯Ό λΆ™μ—¬μ„œ μ•„λž˜μ™€ 같이 μ‚¬μš©ν•˜λ©°, ν˜„μž¬ μ‚¬μš© 쀑인 ν”„λ‘œμ„Έμ„œ μ—”λ””μ•ˆ ν™˜κ²½μ— 맞게 λ³€ν™˜ν•΄μ£ΌλŠ” λ§€ν¬λ‘œλ„ μ•„λž˜μ™€ 같이 μ œκ³΅ν•˜κ³  μžˆλ‹€.

#include <linux/kernel.h>

void le16_to_cpus(__u16 *);
void le32_to_cpus(__u32 *);
void le64_to_cpus(__u64 *);

void cpu_to_le16s(__u16 *);
void cpu_to_le32s(__u32 *);
void cpu_to_le64s(__u64 *);

void be16_to_cpus(__u16 *);
void be32_to_cpus(__u32 *);
void be64_to_cpus(__u64 *);

void cpu_to_be16s(__u16 *);
void cpu_to_be32s(__u32 *);
void cpu_to_be64s(__u64 *);

__u16	le16_to_cpup(const __le16 *);
__u32	le32_to_cpup(const __le32 *);
__u64	le64_to_cpup(const __le64 *);

__le16	cpu_to_le16p(const __u16 *);
__le32	cpu_to_le32p(const __u32 *);
__le64	cpu_to_le64p(const __u64 *);

__u16	be16_to_cpup(const __be16 *);
__u32	be32_to_cpup(const __be32 *);
__u64	be64_to_cpup(const __be64 *);

__be16	cpu_to_be16p(const __u16 *);
__be32	cpu_to_be32p(const __u32 *);
__be64	cpu_to_be64p(const __u64 *);

좜처 πŸ”—