C 빅 엔디안 또는 리틀 엔디안 머신을 결정하는 매크로 정의?


107

기계의 엔디안을 결정하는 한 줄 매크로 정의가 있습니까? 다음 코드를 사용하고 있지만 매크로로 변환하면 너무 길어집니다.

unsigned char test_endian( void )
{
    int test_var = 1;
    unsigned char *test_endian = (unsigned char*)&test_var;

    return (test_endian[0] == 0);
}

2
같은 코드를 매크로에 포함하지 않는 이유는 무엇입니까?
sharptooth

4
C 전 처리기만으로는 엔디안을 이식성있게 결정할 수 없습니다. 최종 테스트 0대신 원 NULL하고 test_endian객체 중 하나를 다른 것으로 변경하십시오 :-).
Alok Singhal

2
또한 매크로가 필요한 이유는 무엇입니까? 인라인 함수는 똑같이 할 것이고 훨씬 더 안전합니다.
sharptooth

13
@Sharptooth, 매크로는 그 값이 컴파일 타임에 알려질 수 있기 때문에 매력적입니다. 즉, 플랫폼의 엔디안을 사용하여 템플릿 인스턴스화를 제어하거나 #if지시문을 사용하여 다른 코드 블록을 선택할 수도 있습니다.
Rob Kennedy

3
사실이지만 비효율적입니다. 리틀 엔디안 CPU가 있고 와이어 나 파일에 리틀 엔디안 데이터를 쓰는 경우에는 데이터를 풀고 재 포장하는 것을 목적없이 피하는 편이 낫습니다. 저는 생계를 위해 비디오 드라이버를 작성했습니다. 가능한 모든 위치를 최적화하기 위해 비디오 카드에 픽셀을 쓸 때 매우 중요합니다.
Edward Falk

답변:


102

임의의 바이트 순서를 지원하는 코드로 다음과 같은 파일에 넣을 준비가되었습니다 order32.h.

#ifndef ORDER32_H
#define ORDER32_H

#include <limits.h>
#include <stdint.h>

#if CHAR_BIT != 8
#error "unsupported char size"
#endif

enum
{
    O32_LITTLE_ENDIAN = 0x03020100ul,
    O32_BIG_ENDIAN = 0x00010203ul,
    O32_PDP_ENDIAN = 0x01000302ul,      /* DEC PDP-11 (aka ENDIAN_LITTLE_WORD) */
    O32_HONEYWELL_ENDIAN = 0x02030001ul /* Honeywell 316 (aka ENDIAN_BIG_WORD) */
};

static const union { unsigned char bytes[4]; uint32_t value; } o32_host_order =
    { { 0, 1, 2, 3 } };

#define O32_HOST_ORDER (o32_host_order.value)

#endif

다음을 통해 리틀 엔디안 시스템을 확인합니다.

O32_HOST_ORDER == O32_LITTLE_ENDIAN

11
그래도 런타임까지 엔디안 을 결정할 수는 없습니다 . 다음은 컴파일에 실패합니다. / ** isLittleEndian :: result-> 0 또는 1 * / struct isLittleEndian {enum isLittleEndianResult {result = (O32_HOST_ORDER == O32_LITTLE_ENDIAN)}; };
user48956 2010-08-13

3
런타임까지 결과를 얻는 것이 불가능합니까?
k06a 2010

8
char? uint8_t이 유형을 사용할 수없는 경우 더 잘 사용 하고 실패합니다 (에서 확인할 수 있음 #if UINT8_MAX). 참고 CHAR_BIT독립적이다 uint8_t.
Andreas Spindler

2
이것은 C ++의 UB입니다 : stackoverflow.com/questions/11373203/…
Lyberta

3
나 완성도, 믹스에 더 많은 일을 던져 보자O32_HONEYWELL_ENDIAN = 0x02030001ul /* Honeywell 316 */
에드워드 포크

49

C99 복합 리터럴을 지원하는 컴파일러가있는 경우 :

#define IS_BIG_ENDIAN (!*(unsigned char *)&(uint16_t){1})

또는:

#define IS_BIG_ENDIAN (!(union { uint16_t u16; unsigned char c; }){ .u16 = 1 }.c)

그러나 일반적으로 호스트 플랫폼의 엔디안에 의존하지 않는 코드를 작성해야합니다.


호스트 엔디안 독립 구현 예 ntohl():

uint32_t ntohl(uint32_t n)
{
    unsigned char *np = (unsigned char *)&n;

    return ((uint32_t)np[0] << 24) |
        ((uint32_t)np[1] << 16) |
        ((uint32_t)np[2] << 8) |
        (uint32_t)np[3];
}

3
"호스트 플랫폼의 엔디안에 의존하지 않는 코드를 작성해야합니다". 불행히도 "우리가 POSIX 호환성 계층을 작성하고 있다는 것을 알고 있지만 호스트 플랫폼의 엔디안성에 따라 다르기 때문에 ntoh를 구현하고 싶지 않습니다."라는 애원은 항상 귀머거리가되었습니다 ;-). 그래픽 형식 처리 및 변환 코드는 제가 본 또 다른 주요 후보입니다. 모든 것이 항상 ntohl을 호출하는 것을 기반으로하고 싶지는 않습니다.
Steve Jessop

5
ntohl호스트 플랫폼의 엔디안에 의존하지 않는 방식으로 구현할 수 있습니다 .
caf

1
@caf 호스트 엔디안 독립 방식으로 ntohl을 어떻게 작성 하시겠습니까?
Hayri Uğur Koltuk

3
@AliVeli : 대답에 예제 구현을 추가했습니다.
caf

6
또한 기록에 추가해야합니다. "(* (uint16_t *)"\ 0 \ xff "<0x100)"는 최소한 gcc 4.5.2를 사용하여 얼마나 최적화하더라도 상수로 컴파일되지 않습니다. 항상 실행 가능한 코드를 생성합니다.
Edward Falk 2012

43

표준 <endian.h>은 없지만를 포함한 많은 시스템 에서 찾을 수있는 몇 가지 정의를 제공합니다.


30
#if __BYTE_ORDER == __LITTLE_ENDIAN및로 엔디안을 테스트합니다 #elif __BYTE_ORDER == __BIG_ENDIAN. 그리고 #errorelsewise를 생성 하십시오.
To1ne 2011 년

6
<endian.h>Windows에서 사용할 수 없습니다
rustyx

2
AndroidChromium 프로젝트 는 또는 정의 endian.h되지 않은 경우 사용 합니다. __APPLE___WIN32
patryk.beza

1
OpenBSD 6.3에서 <endian.h>는 이름 앞에 밑줄이없는 #if BYTE_ORDER == LITTLE_ENDIAN(또는 BIG_ENDIAN)을 제공 합니다. _BYTE_ORDER시스템 헤더 전용입니다. __BYTE_ORDER존재하지 않는다.
George Koehler 2018

@ To1ne Windows (적어도 현재)는 x86 및 ARM 시스템에서만 실행되므로 Endianness가 Windows와 관련이 있다고 의심합니다. x86은 항상 LE이고 ARM은 두 아키텍처 중 하나를 사용하도록 구성 할 수 있습니다.
SimonC

27

런타임에 엔디안을 감지하려면 메모리를 참조 할 수 있어야합니다. 표준 C를 고수하는 경우 메모리에서 변수를 선언하려면 문이 필요하지만 값을 반환하려면식이 필요합니다. 단일 매크로에서이 작업을 수행하는 방법을 모르겠습니다. 이것이 gcc에 확장자가있는 이유입니다. :-)

.h 파일을 갖고 싶다면 다음을 정의 할 수 있습니다.

static uint32_t endianness = 0xdeadbeef; 
enum endianness { BIG, LITTLE };

#define ENDIANNESS ( *(const char *)&endianness == 0xef ? LITTLE \
                   : *(const char *)&endianness == 0xde ? BIG \
                   : assert(0))

그런 다음 원하는대로 ENDIANNESS매크로를 사용할 수 있습니다 .


6
크고 작은 엔디안의 존재를 인정하기 때문에 좋아합니다.
Alok Singhal

6
말하자면 한 가지 유형의 저장소 표현 만 테스트하므로 INT_ENDIANNESS 또는 UINT32_T_ENDIANNESS 매크로를 호출하는 것이 좋습니다. 적분 유형이 리틀 엔디안이지만 double이 미들 엔디안 인 ARM ABI가 있습니다 (각 단어는 리틀 엔디안이지만 부호 비트가있는 단어가 다른 단어 앞에옵니다). 이로 인해 컴파일러 팀은 하루 정도의 흥분을 불러 일으켰습니다.
Steve Jessop

19

전처리기에 만 의존하려면 미리 정의 된 기호 목록을 찾아야합니다. 전 처리기 산술에는 주소 지정 개념이 없습니다.

Mac의 GCC 는 __LITTLE_ENDIAN__또는__BIG_ENDIAN__

$ gcc -E -dM - < /dev/null |grep ENDIAN
#define __LITTLE_ENDIAN__ 1

그런 다음 #ifdef _WIN32등 플랫폼 감지를 기반으로 더 많은 전 처리기 조건 지시문을 추가 할 수 있습니다 .


6
GCC 4.0.1 및 4.2.1이 매킨토시에서 정의하지만 Linux의 GCC 4.1.2는 이러한 매크로를 정의하지 않습니다. 따라서 사용할 컴파일러를 지정할 수있는 경우에도 플랫폼 간 개발을위한 신뢰할 수있는 방법이 아닙니다.
Rob Kennedy

1
오, 그것은 Mac에서 GCC에 의해서만 정의 되었기 때문입니다.
Gregory Pakosz

참고 : My GCC (Mac의 경우)는 #define __BIG_ENDIAN__ 1#define _BIG_ENDIAN 1.

OpenBSD / amd64 용 clang 5.0.1에는 #define __LITTLE_ENDIAN__ 1. 이 매크로는 gcc 기능이 아닌 clang 기능인 것 같습니다. gcc일부 Mac 의 명령은 gcc가 아니라 clang입니다.
George Koehler

Mac의 GCC 4.2.1은 당시 GCC였습니다
Gregory Pakosz 2018

15

나는 이것이 요구 된 것이라고 믿는다. 나는 msvc 아래의 little endian 시스템에서만 이것을 테스트했습니다. 누군가가 빅 엔디안 머신에서 확인합니다.

    #define LITTLE_ENDIAN 0x41424344UL 
    #define BIG_ENDIAN    0x44434241UL
    #define PDP_ENDIAN    0x42414443UL
    #define ENDIAN_ORDER  ('ABCD') 

    #if ENDIAN_ORDER==LITTLE_ENDIAN
        #error "machine is little endian"
    #elif ENDIAN_ORDER==BIG_ENDIAN
        #error "machine is big endian"
    #elif ENDIAN_ORDER==PDP_ENDIAN
        #error "jeez, machine is PDP!"
    #else
        #error "What kind of hardware is this?!"
    #endif

부수적으로 (컴파일러 별), 공격적인 컴파일러를 사용하면 "데드 코드 제거"최적화를 사용하여 다음과 같은 컴파일 시간 #if과 동일한 효과를 얻을 수 있습니다.

    unsigned yourOwnEndianSpecific_htonl(unsigned n)
    {
        static unsigned long signature= 0x01020304UL; 
        if (1 == (unsigned char&)signature) // big endian
            return n;
        if (2 == (unsigned char&)signature) // the PDP style
        {
            n = ((n << 8) & 0xFF00FF00UL) | ((n>>8) & 0x00FF00FFUL);
            return n;
        }
        if (4 == (unsigned char&)signature) // little endian
        {
            n = (n << 16) | (n >> 16);
            n = ((n << 8) & 0xFF00FF00UL) | ((n>>8) & 0x00FF00FFUL);
            return n;
        }
        // only weird machines get here
        return n; // ?
    }

위는 완전히 내 코드를 제거, 컴파일러는 컴파일시에 상수 값을 인식한다는 사실에 의존 if (false) { ... }와 같은 대체합니다 코드 if (true) { foo(); }foo();최악의 시나리오 : 최적화를하지 않는 컴파일러는, 당신은 여전히 약간 느린 올바른 코드 만 얻을.


나는이 방법을 좋아하지만 내가 틀렸다면 정정한다. 이것은 당신이 만들고있는 머신에서 컴파일 할 때만 작동한다. 맞아?
leetNightshade

3
gcc는 또한 다중 문자 문자 상수로 인해 오류를 발생시킵니다. 따라서 휴대용이 아닙니다.
Edward Falk 2012

2
어떤 컴파일러가 당신에게 쓰기를 허용 'ABCD'합니까?
Ryan Haining

2
많은 컴파일러가 완화 된 준수 모드에서 멀티 바이트 문자 상수를 허용하지만 맨 위 부분을 실행 clang -Wpedantic -Werror -Wall -ansi foo.c하면 오류가 발생합니다. (연타이 구체적으로 : -Wfour-char-constants -Werror)

@Edward Falk 코드에 다중 문자 상수가있는 것은 오류아닙니다 . 구현 정의 동작 C11 6.4.4.4입니다. 10. gcc 및 기타 설정에 따라 경고 / 오류가 발생할 수 있습니다. 그러나 C 오류는 아닙니다. 다중 문자 문자 상수를 사용하는 것은 확실히 인기가 없습니다.
chux - 분석 재개 모니카

10

컴파일 시간 테스트를 찾고 있고 gcc를 사용하는 경우 다음을 수행 할 수 있습니다.

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__

자세한 정보는 gcc 문서 를 참조하십시오.


3
이것은 확실히 GCC를 사용하는 모든 사용자를위한 최고의 답변입니다
rtpax

2
__BYTE_ORDER__GCC 4.6부터 사용 가능
Benoit Blanchon 18.03.30

8

실제로 복합 리터럴 (C99)을 사용하여 임시 개체의 메모리에 액세스 있습니다 .

#define IS_LITTLE_ENDIAN (1 == *(unsigned char *)&(const int){1})

컴파일 타임에 평가할 GCC.


나는 그것을 좋아한다. C99에서 컴파일하고 있음을 알 수있는 이식 가능한 컴파일 타임 방법이 있습니까?
Edward Falk

1
아, GCC가 아니라면?
Edward Falk 2016 년

1
@EdwardFalk 예. #if __STDC_VERSION__ >= 199901L.
Jens

7

'C 네트워크 라이브러리'는 엔디안을 처리하는 기능을 제공합니다. 즉, htons (), htonl (), ntohs () 및 ntohl () ... 여기서 n은 "network"(즉, big-endian)이고 h는 "host"(즉, 암호).

이러한 명백한 '함수'는 (일반적으로) 매크로로 정의되므로 [<netinet / in.h> 참조], 사용에 대한 런타임 오버 헤드가 없습니다.

다음 매크로는 이러한 '함수'를 사용하여 엔디안을 평가합니다.

#include <arpa/inet.h>
#define  IS_BIG_ENDIAN     (1 == htons(1))
#define  IS_LITTLE_ENDIAN  (!IS_BIG_ENDIAN)

게다가:

시스템의 엔디안을 알아야하는 유일한 시간은 알 수없는 엔디안의 다른 시스템에서 읽을 수있는 변수를 [파일 / 기타에] 쓸 때입니다 (플랫폼 간 호환성을 위해 ) ... 다음과 같은 경우 엔디안 함수를 직접 사용하는 것이 좋습니다.

#include <arpa/inet.h>

#define JPEG_MAGIC  (('J'<<24) | ('F'<<16) | ('I'<<8) | 'F')

// Result will be in 'host' byte-order
unsigned long  jpeg_magic = JPEG_MAGIC;

// Result will be in 'network' byte-order (IE. Big-Endian/Human-Readable)
unsigned long  jpeg_magic = htonl(JPEG_MAGIC);

이것은 엔디안을 결정하는 빠른 방법을 찾고 있던 질문에 실제로 대답하지 않습니다.
Oren

@Oren : 귀하의 유효한 비판과 관련하여 원래 질문을 더 직접적으로 다루는 세부 사항을 앞에 추가했습니다.
BlueChip

6

매크로보다는 인라인 함수를 사용하십시오. 게다가 매크로의 좋지 않은 부작용 인 메모리에 무언가를 저장해야합니다.

다음과 같이 정적 또는 전역 변수를 사용하여 짧은 매크로로 변환 할 수 있습니다.

static int s_endianess = 0;
#define ENDIANESS() ((s_endianess = 1), (*(unsigned char*) &s_endianess) == 0)

가장 단순하기 때문에 이것이 최고라고 생각합니다. 그러나 그것은 혼합 엔디안에 대해 테스트하지 않습니다
Hayri UGUR Koltuk에게

1
s_endianess시작하기 위해 1로 설정 되지 않은 이유는 무엇 입니까?
SquareRootOfTwentyThree

5

이식 가능한 #define 또는 의존 할 것이 없지만 플랫폼은 '호스트'엔디안으로 /에서 변환하는 표준 기능을 제공합니다.

일반적으로 BIG 엔디안 인 '네트워크 엔디안'을 사용하여 디스크 또는 네트워크로 스토리지를 수행 하고 호스트 엔디안 (x86에서는 LITTLE 엔디안)을 사용하여 로컬 계산을 수행합니다 . htons()ntohs()및 친구를 사용 하여 둘 사이를 변환합니다.


4
#include <stdint.h>
#define IS_LITTLE_ENDIAN (*(uint16_t*)"\0\1">>8)
#define IS_BIG_ENDIAN (*(uint16_t*)"\1\0">>8)

6
이것은 또한 상수가 아닌 실행 가능한 코드를 생성합니다. 당신은 "IS_BIG_ENDIAN #IF"할 수 없었다
에드워드 포크에게

내가 이해하는 한 C / C ++ 표준 정의되지 않은 동작에 의존하지 않기 때문에이 솔루션을 좋아합니다. 컴파일 시간이 아니지만이를위한 유일한 표준 솔루션은 c ++ 20 std :: endian
ceztko

4

엔디안이 전체 이야기가 아니라는 점을 잊지 마십시오. 크기 char가 8 비트 (예 : DSP)가 아닐 수도 있고, 2의 보수 부정이 보장되지 않을 수 있으며 (예 : Cray), 엄격한 정렬이 필요할 수 있습니다 (예 : SPARC, ARM도 중간으로 스프링). - 정렬되지 않은 경우 엔디안 ) 등

특정 CPU 아키텍처 를 대상으로하는 것이 더 나은 아이디어 일 수 있습니다.대신 .

예를 들면 :

#if defined(__i386__) || defined(_M_IX86) || defined(_M_IX64)
  #define USE_LITTLE_ENDIAN_IMPL
#endif

void my_func()
{
#ifdef USE_LITTLE_ENDIAN_IMPL
  // Intel x86-optimized, LE implementation
#else
  // slow but safe implementation
#endif
}

불행히도이 솔루션은 컴파일러 별 정의에 의존하기 때문에 휴대 성이 뛰어나지 않습니다 (표준은 없지만 여기 에 이러한 정의에 대한 멋진 컴파일이 있습니다).


3

이 시도:

#include<stdio.h>        
int x=1;
#define TEST (*(char*)&(x)==1)?printf("little endian"):printf("Big endian")
int main()
{

   TEST;
}

2

오늘날 컴파일러는 컴파일 시간 (최적화에 따라 다름)에서 해당 답변을 평가하고 특정 엔디안을 기반으로 특정 값을 반환하지만 실제 머신 엔디안은 다를 수 있으므로 여기에있는 대부분의 답변은 이식 가능하지 않습니다. 엔디안이 테스트되는 값은 시스템 메모리에 도달하지 않으므로 실제 실행 된 코드는 실제 엔디안에 관계없이 동일한 결과를 반환합니다.

예를 들어 ARM Cortex-M3에서 구현 된 엔디안은 상태 비트 AIRCR.ENDIANNESS에 반영되며 컴파일러는 컴파일 타임에이 값을 알 수 없습니다.

여기에 제안 된 일부 답변에 대한 컴파일 출력 :

https://godbolt.org/z/GJGNE2 에 대한 대답은,

https://godbolt.org/z/Yv-pyJ 에 대한 답변은 .

이를 해결하려면 volatile한정자 를 사용해야합니다 . Yogeesh H T의 대답은 오늘날의 실제 사용에 대한 가장 가까운 하나이지만, 이후 Christoph더 포괄적 인 솔루션을 제안, 자신에 대한 약간의 수정 대답은 대답이 완료, 단지 추가 할 것이다 volatile노조의 선언 : static const volatile union.

이것은 엔디안을 결정하는 데 필요한 메모리에서 저장하고 읽는 것을 보장합니다.


2

전처리기를 덤프하는 경우 #defines

gcc -dM -E - < /dev/null
g++ -dM -E -x c++ - < /dev/null

일반적으로 도움이되는 자료를 찾을 수 있습니다. 컴파일 타임 로직.

#define __LITTLE_ENDIAN__ 1
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__

그러나 다양한 컴파일러는 다른 정의를 가질 수 있습니다.


0

내 대답은 질문과는 다르지만 시스템이 리틀 엔디안인지 빅 엔디안인지 찾는 것이 정말 간단 합니까?

암호:

#include<stdio.h>

int main()
{
  int a = 1;
  char *b;

  b = (char *)&a;
  if (*b)
    printf("Little Endian\n");
  else
    printf("Big Endian\n");
}

0

시스템이 리틀 엔디안인지 빅 인디언인지 확인하기위한 C 코드입니다.

int i = 7;
char* pc = (char*)(&i);
if (pc[0] == '\x7') // aliasing through char is ok
    puts("This system is little-endian");
else
    puts("This system is big-endian");

-3

엔디안을 찾는 매크로

#define ENDIANNES() ((1 && 1 == 0) ? printf("Big-Endian"):printf("Little-Endian"))

또는

#include <stdio.h>

#define ENDIAN() { \
volatile unsigned long ul = 1;\
volatile unsigned char *p;\
p = (volatile unsigned char *)&ul;\
if (*p == 1)\
puts("Little endian.");\
else if (*(p+(sizeof(unsigned long)-1)) == 1)\
puts("Big endian.");\
else puts("Unknown endian.");\
}

int main(void) 
{
       ENDIAN();
       return 0;
}

3
첫 번째 매크로는 올바르지 않으며 항상 "Big-Endian"을 반환합니다. 비트 시프트는 엔디안의 영향을받지 않습니다. 엔디안은 메모리에 대한 읽기 및 저장에만 영향을줍니다.
GaspardP
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.