programing

엔디안니스 탐지

itmemos 2023. 10. 4. 20:59
반응형

엔디안니스 탐지

저는 현재 타겟 시스템의 엔디안성이 무엇이든 입출력을 적절하게 처리하는 C 소스 코드를 만들고자 합니다.

I/O 규약으로 "little endian"을 선택했습니다. 즉, big endian CPU의 경우 쓰기 또는 읽기 중에 데이터를 변환해야 합니다.

전환은 문제가 아닙니다.제가 직면한 문제는 (CPU는 실행 도중에 엔디안을 변경하지 않기 때문에) 엔디안을 감지하는 것입니다.

지금까지 이걸 사용해 왔습니다.

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
...
#else
...
#endif

GCC 사전 정의 매크로로 문서화되어 있고, 비주얼도 이를 이해하고 있는 것 같습니다.

그러나 일부 big_endian 시스템(PowerPC)에 대해 검사가 실패한다는 보고를 받았습니다.

그래서, 컴파일러와 타겟 시스템이 무엇이든 간에, 엔디아니스가 정확하게 감지되도록 보장하는, 완벽한 해결책을 찾고 있습니다.대부분은 적어도...

[편집] : 제안된 솔루션의 대부분은 "run-time tests"에 의존합니다.이러한 테스트는 컴파일 중에 컴파일러가 적절히 평가할 수 있으므로 실제 런타임 성능이 전혀 들지 않습니다.

어떤 , 의 <<> 로으로 if (0) { ... } else { ... }>>으로 충분하지 않습니다.현재 코드 구현에서 변수 및 함수 선언은 big_endian 검출에 의존합니다.if 문으로는 변경할 수 없습니다.

분명히 비밀번호를 다시 쓰는 계획이 있습니다.백 계획이 계획은...

그런 건 피하고 싶지만, 그게 점점 줄어드는 희망인 것 같네요...

[편집 2] : 코드를 깊게 수정하여 "런타임 테스트"를 테스트했습니다.이러한 테스트는 업무를 올바르게 수행하지만 성능에도 영향을 미칩니다.

저는 테스트 결과가 예측 가능하기 때문에 컴파일러가 나쁜 분기를 제거할 수 있을 것이라고 예상했습니다.하지만 불행히도, 항상 작동하지는 않습니다.MSVC는 좋은 컴파일러이고, 나쁜 분기를 제거하는 데는 성공하지만, GCC는 버전, 테스트 종류에 따라 결과가 엇갈리고, 32비트보다 64비트에 더 큰 영향을 미칩니다.

이거 이상하네.또한 런타임 테스트가 컴파일러에 의해 처리되도록 보장될 수 없다는 것을 의미합니다.

편집 3 : 요즘 컴파일러가 확실한 yes/no 신호로 풀어줄 것을 기대하며 컴파일 타임 상수 조합을 사용하고 있습니다.그리고 그것은 꽤 잘 작동합니다: https://godbolt.org/g/DAafKo

앞서 밝힌 바와 같이 빅 엔디안을 탐지할 수 있는 유일한 "진짜" 방법은 런타임 테스트를 사용하는 것입니다.

그러나 때로는 매크로가 선호될 수도 있습니다.

불행하게도, 저는 이런 상황을 감지할 수 있는 "테스트"를 단 한 번도 발견하지 못했고, 그 테스트들을 모아놓은 것도 발견하지 못했습니다.

를 들어 , GCC 는합니다를 합니다.__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__이 값를 보다 .전러)에서는 NULL= NULL값 "true"합니다입니다. 따라서 보다 완벽한 버전이 필요합니다.defined(__BYTE_ORDER__)&&(__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)

좋아요, 이제 최신 GCC에서는 작동하지만 다른 컴파일러는 어떤가요?

해봐도 좋습니다.__BIG_ENDIAN__아니면__BIG_ENDIAN아니면_BIG_ENDIAN빅 엔디언 컴파일러에 정의되는 경우가 많습니다.

이렇게 하면 탐지력이 향상됩니다.하지만 특별히 PowerPC 플랫폼을 대상으로 한다면 몇 가지 테스트를 더 추가하여 더 많은 탐지 기능을 향상시킬 수 있습니다._ARCH_PPC아니면__PPC__아니면__PPC아니면PPC아니면__powerpc__아니면__powerpc또는 심지어powerpc이 빅 할 수 이 모든 정의를 결합하면 컴파일러와 버전에 상관없이 빅 엔디언 시스템과 특히 powerpc를 감지할 수 있습니다.

따라서 정리하자면, 모든 플랫폼과 컴파일러에서 빅 엔디언 CPU를 검출하는 것을 보장하는 "표준 사전 정의 매크로"와 같은 것은 없지만, 대부분의 상황에서 빅 엔디언을 정확하게 검출할 수 있는 높은 확률을 제공하는 사전 정의 매크로는 많이 있습니다.

C 의 를 하는 것 을 할 수 .#define C C 에 .

그러나 컴파일할 때 수행된 가정이 참인지 확인하기 위해 프로그램 시작 시 수행되는 주장을 추가할 수 있습니다.

inline int IsBigEndian()
{
    int i=1;
    return ! *((char *)&i);
}

/* ... */

#ifdef COMPILED_FOR_BIG_ENDIAN
assert(IsBigEndian());
#elif COMPILED_FOR_LITTLE_ENDIAN
assert(!IsBigEndian());
#else
#error "No endianness macro defined"
#endif

()COMPILED_FOR_BIG_ENDIAN그리고.COMPILED_FOR_LITTLE_ENDIAN입니다.#defined ).

컴파일 타임 검사 대신 빅 엔디안 오더(많은 사람들이 "네트워크 오더"로 간주하는)를 사용하고 대부분의 UNIX 시스템과 Windows에서 제공하는 //htonlntohsntohl 기능을 사용하는 것이 좋습니다.그들은 이미 당신이 하려는 일을 하도록 규정되어 있습니다.바퀴를 다시 만드는 이유는 무엇입니까?

다음과 같은 방법을 시도해 보십시오.

if(*(char *)(int[]){1}) {
    /* little endian code */
} else {
    /* big endian code */
}

컴파일 시 컴파일러가 해결하는지 확인합니다.그렇지 않다면, 조합과 같은 일을 하는 것이 더 좋은 운이 될 수도 있습니다.록 0,1는 1,0각)다에 것과 할 수 0을 하는 것을 .buf[HI]그리고.buf[LO].

컴파일러 정의 매크로에도 불구하고, 아키텍처의 엔디안을 결정하는 것은 데이터를 메모리에 저장하는 방식을 분석하는 것이기 때문에 이를 탐지할 수 있는 컴파일 타임 방법은 없다고 생각합니다.

다음과 같은 기능이 있습니다.

bool IsLittleEndian () {

    int i=1;

    return (int)*((unsigned char *)&i)==1;

}

다른 사람들이 지적한 것처럼 컴파일 타임에 엔디안을 확인할 수 있는 휴대용 방법은 없습니다.하지만, 한가지 방법은 다음과 같은 방법을 사용하는 것입니다.autoconf시스템이 빅 엔디안인지 리틀 엔디안인지 탐지하기 위한 빌드 스크립트의 일부로 도구를 사용합니다.AC_C_BIGENDIAN이 정보를 저장하는 macro.어떤 의미에서, 이것은 시스템이 빅 엔디언인지 리틀 엔디언인지를 런타임에 감지하는 프로그램을 구축하고, 이후 메인 소스 코드에 의해 정적으로 사용될 수 있는 프로그램 출력 정보를 갖습니다.

도움이 되길 바랍니다!

이것은 C: Pointers의 45페이지에서 나온 것입니다.

#include <stdio.h>
#define BIG_ENDIAN 0
#define LITTLE_ENDIAN 1

int endian()
{
   short int word = 0x0001;
   char *byte = (char *) &word;
   return (byte[0] ? LITTLE_ENDIAN : BIG_ENDIAN);
}

int main(int argc, char* argv[])
{
   int value;
   value = endian();
   if (value == 1)
      printf("The machine is Little Endian\n");
   else
      printf("The machine is Big Endian\n");
   return 0;
}

소켓의ntohl함수는 이 목적을 위해 사용될 수 있습니다.출처

// Soner
#include <stdio.h>
#include <arpa/inet.h>


int main() {
    if (ntohl(0x12345678) == 0x12345678) {
        printf("big-endian\n");
    } else if (ntohl(0x12345678) == 0x78563412) {
        printf("little-endian\n");
    } else {
        printf("(stupid)-middle-endian\n");
    }
    return 0;
}

저의 GCC 버전은 9.3.0이며 powerpc64 플랫폼을 지원하도록 구성되어 있으며 테스트를 통해 다음과 같은 매크로 로직을 지원하는지 확인했습니다.

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
......
#endif
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
.....
#endif

C++20부터는 더 이상의 해킹이나 컴파일러 확장이 필요 없습니다.

https://en.cppreference.com/w/cpp/types/endian

std::endian(헤더에 정의됨)<bit>)

enum class endian
{
    little = /*implementation-defined*/,
    big    = /*implementation-defined*/,
    native = /*implementation-defined*/
};
  • 모든 스칼라 유형이 리틀 엔디안인 경우 std::endian::native는 std::endian::little과 같습니다.

  • 모든 스칼라 유형이 big-endian이면 std::endian::native는 std::endian:::big와 같습니다.

컴파일 시에 모든 컴파일러에서 휴대용으로 사용할 수는 없습니다.어쩌면 런타임에 코드를 변경할 수도 있습니다. 이것은 달성할 수 있습니다.

전처리기 지시가 있는 C에서는 엔디안니스를 휴대용으로 검출할 수 없습니다.

제가 자유롭게 인용된 글을 다시 포맷했습니다.

2017-07-18 현재 사용하고 있는union { unsigned u; unsigned char c[4]; }

한다면sizeof (unsigned) != 4당신의 시험은 실패할지도 모릅니다.

사용하는 것이 더 나을 수 있습니다.

union { unsigned u; unsigned char c[sizeof (unsigned)]; }

대부분의 사람들이 언급했듯이 컴파일 타임이 최선의 방법입니다.교차 컴파일을 수행하지 않고 다음을 사용한다고 가정합니다.cmake합니다와 같은 합니다.) 작동합니다.configure스크립트)를 사용하면 컴파일된 .c 또는 .cpp 파일로 실행 중인 프로세서의 실제 검증된 엔디안을 제공하는 사전 테스트를 사용할 수 있습니다.

와 함께cmake매크로를 사용합니다.변수를 설정하여 소프트웨어에 전달할 수 있습니다.이와 같은 것(검증되지 않음):

TestBigEndian(IS_BIG_ENDIAN)
...
set(CFLAGS ${CFLAGS} -DIS_BIG_ENDIAN=${IS_BIG_ENDIAN}) // C
set(CXXFLAGS ${CXXFLAGS} -DIS_BIG_ENDIAN=${IS_BIG_ENDIAN}) // C++

의 C/C++ 를 할 수 .IS_BIG_ENDIAN정의:

#if IS_BIG_ENDIAN
    ...do big endian stuff here...
#else
    ...do little endian stuff here...
#endif

따라서 이러한 테스트의 가장 큰 문제는 교차 컴파일입니다. 왜냐하면 다른 엔디안성과 함께 완전히 다른 CPU에 있을 수 있기 때문입니다.하지만 적어도 나머지 코드를 컴파일할 때에는 엔디안을 제공하고 대부분의 프로젝트에서 작동할 것입니다.

저는 C에서 전처리기 없이 모든 C 타입에 대한 엔디안니스를 계산하는 런타임만 사용하는 일반적인 접근 방식을 제공했습니다.

Linux x86_64 아키텍처의 출력은 다음과 같습니다.

fabrizio@toshibaSeb:~/git/pegaso/scripts$ gcc -o sizeof_endianess sizeof_endianess.c 
fabrizio@toshibaSeb:~/git/pegaso/scripts$ ./sizeof_endianess 
INTEGER TYPE  | signed  |  unsigned  | 0x010203...             | Endianess
--------------+---------+------------+-------------------------+--------------
int           |  4      |      4     | 04 03 02 01             | little
char          |  1      |      1     | -                       | -
short         |  2      |      2     | 02 01                   | little
long int      |  8      |      8     | 08 07 06 05 04 03 02 01 | little
long long int |  8      |      8     | 08 07 06 05 04 03 02 01 | little
--------------+---------+------------+-------------------------+--------------
FLOATING POINT| size    |
--------------+---------+
float         |  4
double        |  8
long double   | 16

출처: https://github.com/bzimage-it/pegaso/blob/master/scripts/sizeof_endianess.c

이 방법은 컴파일 시(가능하지 않음)에 엔디언스를 감지하지 못하거나 다른 엔디언스를 배제한다고 가정하지 않는 것이 보다 일반적인 접근 방법입니다.사실 엔디안니스는 아키텍처/프로세서의 개념이 아니라 단일 유형을 고려한다는 점을 언급하는 것이 중요합니다.예를 들어 https://stackoverflow.com/a/4712594/3280080 PDP-11의 @Christoph에 의해 주장된 바와 같이, 동시에 다른 내생성을 가질 수 있습니다.

를 됩니다.x = 0x010203...길이가 긴 다음 주소를 하나씩 증가시키는 단일 바이트로 주조된 것을 보고 인쇄합니다.

누가엔디안 및/또는 혼합 엔디안으로 테스트해 줄 수 있습니까?

제가 파티에 늦었다는 것을 알지만, 여기 제 의견이 있습니다.

int is_big_endian() {
    return 1 & *(uint16_t*)"01";
}

은 이 한 것입니다.'0'으로는 48입니다.'1'49, 그래서'1'되어 있는 LSB는다.'0'뇨로 줄 수 '\x00'그리고.'\x01'근데 제 버전이 더 가독성이 좋은 것 같아요.

#define BIG_ENDIAN ((1 >> 1 == 0) ? 0 : 1)

언급URL : https://stackoverflow.com/questions/8978935/detecting-endianness

반응형