programing

컴파일 시 엔디안성 판별

goodsources 2022. 7. 30. 19:16
반응형

컴파일 시 엔디안성 판별

프로그램을 컴파일하고 있는 플랫폼의 엔디안을 (컴파일 시간 중에) 안전하게 확인할 수 있는 휴대용 방법이 있습니까?C로 쓰고 있어요.

[편집] 답변 감사합니다.런타임 솔루션을 계속 사용하기로 했습니다!

컴파일 타임 체크에 관한 최초의 질문에 답하기 위해서, 기존의 C, C++, 및 POSIX 규격 중 endianness를 검출하기 위한 매크로를 정의하는 것은 없기 때문에, 기존의 모든 컴파일러와 미래의 모든 컴파일러에서 동작하는 표준화된 방법은 없습니다.

다만, 기존의 컴파일러 세트에 한정하고 싶은 경우는, 각 컴파일러의 메뉴얼을 참조해, 엔디안을 정의하기 위해서 사용하는 미리 정의된 매크로(있는 경우)를 확인할 수 있습니다.이 페이지에는 검색할 수 있는 매크로가 몇 가지 나열되어 있습니다.이러한 매크로에 도움이 되는 코드를 다음에 나타냅니다.

#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \
    defined(__BIG_ENDIAN__) || \
    defined(__ARMEB__) || \
    defined(__THUMBEB__) || \
    defined(__AARCH64EB__) || \
    defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__)
// It's a big-endian target architecture
#elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || \
    defined(__LITTLE_ENDIAN__) || \
    defined(__ARMEL__) || \
    defined(__THUMBEL__) || \
    defined(__AARCH64EL__) || \
    defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__)
// It's a little-endian target architecture
#else
#error "I don't know what architecture this is!"
#endif

컴파일러의 설명서에서 사용하는 정의된 매크로를 찾을 수 없는 경우, 컴파일러가 사전 정의된 매크로의 전체 목록을 내보내고 어떤 매크로가 동작하는지 추측하도록 강제할 수도 있습니다(ENDIAN, ORDER 또는 프로세서 아키텍처 이름이 포함된 매크로를 찾습니다). 페이지에는 다양한 컴파일러에서 이를 수행하는 여러 가지 방법이 나와 있습니다.

Compiler                   C macros                         C++ macros
Clang/LLVM                 clang -dM -E -x c /dev/null      clang++ -dM -E -x c++ /dev/null
GNU GCC/G++                gcc   -dM -E -x c /dev/null      g++     -dM -E -x c++ /dev/null
Hewlett-Packard C/aC++     cc    -dM -E -x c /dev/null      aCC     -dM -E -x c++ /dev/null
IBM XL C/C++               xlc   -qshowmacros -E /dev/null  xlc++   -qshowmacros -E /dev/null
Intel ICC/ICPC             icc   -dM -E -x c /dev/null      icpc    -dM -E -x c++ /dev/null
Microsoft Visual Studio (none)                              (none)
Oracle Solaris Studio      cc    -xdumpmacros -E /dev/null  CC      -xdumpmacros -E /dev/null
Portland Group PGCC/PGCPP  pgcc  -dM -E                     (none)

마지막으로 정리하자면 Microsoft Visual C/C++ 컴파일러는 홀수이며 위의 컴파일러는 없습니다.다행히 여기에서는 미리 정의된 매크로가 문서화되어 있기 때문에 타깃 프로세서 아키텍처를 사용하여 엔디안성을 추론할 수 있습니다.Windows 로 현재 서포트되고 있는 프로세서는 모두 little-endian 입니다._M_IX86,_M_X64,_M_IA64 , , , , 입니다._M_ARMPower PC)와 같이 .PC (리틀엔디안)_M_PPC은 빅 엔디안이었다을 사용하다헤더를 Xbox 360을 않습니다PC 머신이기 때문에 크로스 플랫폼 라이브러리 헤더를 쓰고 있다면 Xbox 360을 체크하는 것이 좋습니다._M_PPC.

CMake TestBigEndian 사용

INCLUDE(TestBigEndian)
TEST_BIG_ENDIAN(ENDIAN)
IF (ENDIAN)
    # big endian
ELSE (ENDIAN)
    # little endian
ENDIF (ENDIAN)

rapidjson 도서관에서 가져왔어요

#define BYTEORDER_LITTLE_ENDIAN 0 // Little endian machine.
#define BYTEORDER_BIG_ENDIAN 1 // Big endian machine.

//#define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN

#ifndef BYTEORDER_ENDIAN
    // Detect with GCC 4.6's macro.
#   if defined(__BYTE_ORDER__)
#       if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
#           define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#       elif (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#           define BYTEORDER_ENDIAN BYTEORDER_BIG_ENDIAN
#       else
#           error "Unknown machine byteorder endianness detected. User needs to define BYTEORDER_ENDIAN."
#       endif
    // Detect with GLIBC's endian.h.
#   elif defined(__GLIBC__)
#       include <endian.h>
#       if (__BYTE_ORDER == __LITTLE_ENDIAN)
#           define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#       elif (__BYTE_ORDER == __BIG_ENDIAN)
#           define BYTEORDER_ENDIAN BYTEORDER_BIG_ENDIAN
#       else
#           error "Unknown machine byteorder endianness detected. User needs to define BYTEORDER_ENDIAN."
#       endif
    // Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro.
#   elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
#       define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#   elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)
#       define BYTEORDER_ENDIAN BYTEORDER_BIG_ENDIAN
    // Detect with architecture macros.
#   elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__)
#       define BYTEORDER_ENDIAN BYTEORDER_BIG_ENDIAN
#   elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__)
#       define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#   elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
#       define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#   else
#       error "Unknown machine byteorder endianness detected. User needs to define BYTEORDER_ENDIAN."
#   endif
#endif

마지막으로 C 프리프로세서의 한 줄 엔디안니스 검출:

#include <stdint.h>

#define IS_BIG_ENDIAN (*(uint16_t *)"\0\xff" < 0x100)

gcc동작합니다.-O1.

입니다.stdint.hC99 입니다.ANSI/C89 의 휴대성에 대해서는, Doug Gwyn 의 Instant C9x 라이브러리를 참조해 주세요.

C99 에서는, 다음과 같이 체크를 실행할 수 있습니다.

#define I_AM_LITTLE (((union { unsigned x; unsigned char c; }){1}).c)

이다, 조건이다.if (I_AM_LITTLE)컴파일 시 평가되며 컴파일러가 블록 전체를 최적화할 수 있습니다.

이것이 C99에서 엄밀하게 말하면 (정적 스토리지 지속 시간 데이터의 이니셜라이저에 사용할 수 있는) 상수 표현인지 여부에 대한 참조는 없습니다.그렇지 않다면 다음으로 좋은 방법입니다.

저는 이 에 대한 .constexprC++의

union Mix {
    int sdat;
    char cdat[4];
};
static constexpr Mix mix { 0x1 };
constexpr bool isLittleEndian() {
    return mix.cdat[0] == 1;
}

★★mixconstexpr이어서 '컴파일 시간'에서 할 수 .constexpr bool isLittleEndian()이치노

갱신하다

아래 @Chersandhth가 지적한 바와 같이 문제가 있는 것 같습니다.

그 이유는 타입 펀닝이 금지된 C++11-Standard 준거가 아니기 때문입니다.한 번에 한 명의 조합원만 활동할 수 있습니다.표준 컴파일러를 사용하면 오류가 발생합니다.

그러니까 C++에서는 사용하지 마세요.C로 할 수 있을 것 같은데.교육상 :-)에 대한 질문이기 때문에 답변은 남겨둡니다.

업데이트 2

은 '하다'라고 합니다.int는 4 이 4 4 니 has 。char. @PetrVep orrectek > @PetrVep orrectlyek 。당신의 코드를 진정으로 휴대하기 위해서는 당신은 여기서 더 똑똑해야 합니다.하지만 이것은 많은 경우에 충분할 것이다.:sizeof(char) 있다1으로는, 「」입니다위의 코드는 다음과 같이 가정합니다.sizeof(int)==4

C FAQ에서 흥미로운 내용을 읽어보십시오.

아마 못 하실 거예요.엔디안성을 검출하기 위한 일반적인 기술에는 포인터 또는 문자 배열, 또는 결합이 포함되지만, 프리프로세서 산술에서는 긴 정수만 사용되며 주소 지정의 개념은 없습니다.또 다른 솔깃한 가능성은 다음과 같습니다.

  #if 'ABCD' == 0x41424344

하지만 이것도 믿을 수 없어요.

컴파일 시간이 아니라 런타임일 수 있습니다.다음은 엔디안성을 판단하기 위해 작성한 C함수입니다.

/*  Returns 1 if LITTLE-ENDIAN or 0 if BIG-ENDIAN  */
#include <inttypes.h>
int endianness()
{
  union { uint8_t c[4]; uint32_t i; } data;
  data.i = 0x12345678;
  return (data.c[0] == 0x78);
}

이런 구조를 사용한 적이 있습니다.

uint16_t  HI_BYTE  = 0,
          LO_BYTE  = 1;
uint16_t  s = 1;

if(*(uint8_t *) &s == 1) {   
   HI_BYTE = 1;
   LO_BYTE = 0;
} 

pByte[HI_BYTE] = 0x10;
pByte[LO_BYTE] = 0x20;

O2를 사용한 gcc는 완전한 컴파일 시간을 만들 수 있었습니다. HI_BYTE그리고.LO_BYTE변수는 완전히 대체되었고 심지어 pByte acces도 어셈블러에서 동등한 값으로 대체되었습니다.*(unit16_t *pByte) = 0x1020;.

컴파일 시간만큼 빠르네요.

컴파일 시간 확인용입니다.

부스트 헤더 파일의 정보를 사용할 수 있습니다.부스트 헤더 파일에는 많은 플랫폼이 포함됩니다.

런타임 확인을 위해 편집

bool isLittleEndian()
{
    short int number = 0x1;
    char *numPtr = (char*)&number;
    return (numPtr[0] == 1);
}

정수를 생성하여 첫 번째 바이트(최소 유효 바이트)를 읽습니다.이 바이트가 1이면 시스템이 little endian이고, 그렇지 않으면 big endian입니다.

생각하고 편집하다

네, 일부 플랫폼에서는 잠재적인 문제가 발생할 수 있습니다(아무것도 생각할 수 없음).sizeof(char) == sizeof(short int). 에서 사용할 수 있는 고정폭 멀티바이트 적분 타입을 사용할 수 있습니다.<stdint.h>또는 플랫폼에 이 기능이 없는 경우 다시 한 번 부스트 헤더를 용도에 맞게 조정할 수 있습니다.

저로서는 중간 접근법을 사용하기로 했습니다. 매크로가 존재하지 않거나 발견되지 않으면 런타임에 실행하세요.GNU 컴파일러에서 동작하는 것은 다음과 같습니다.

#define II      0x4949     // arbitrary values != 1; examples are
#define MM      0x4D4D     // taken from the TIFF standard

int
#if defined __BYTE_ORDER__ && __BYTE_ORDER__ == __LITTLE_ENDIAN
     const host_endian = II;
# elif defined __BYTE_ORDER__ && __BYTE_ORDER__ == __BIG__ENDIAN
     const host_endian = MM;
#else
#define _no_BYTE_ORDER
     host_endian = 1;            // plain "int", not "int const" !
#endif

그리고 실제 코드로:

int main(int argc, char **argv) {
#ifdef _no_BYTE_ORDER
    host_endian = * (char *) &host_endian ? II : MM;
#undef _no_BYTE_ORDER
#endif

// .... your code here, for instance:
printf("Endedness: %s\n", host_endian == II ? "little-endian"
                                            : "big-endian");

return 0;
}

한편, 원래의 포스터에 기재되어 있듯이, 런타임 체크의 오버헤드는 매우 적기 때문에(코드의 2행과 마이크로초) 프리프로세서로 실행해 볼 필요는 없습니다.

내가 알기로는, 컴파일 타임은 아니야.

런타임에는 멀티바이트 값을 기존 비트 문자열로 설정하는 등 사소한 검사를 수행하여 어떤 바이트가 발생하는지 검사할 수 있습니다.예를 들어, 유니언을 사용한다면

typedef union {
    uint32_t word;
    uint8_t bytes[4];
} byte_check;

또는 캐스팅,

uint32_t word;
uint8_t * bytes = &word;

완전히 휴대 가능한 엔디안 검사에서는 빅 엔디안, 리틀 엔디안 및 혼합 엔디안 시스템을 모두 고려해야 합니다.

언급URL : https://stackoverflow.com/questions/4239993/determining-endianness-at-compile-time

반응형