C ++ 프로그램에서 프로그래밍 방식으로 엔디안 감지


211

빅 엔디안 또는 리틀 엔디안 아키텍처인지 여부를 감지하는 프로그래밍 방식이 있습니까? 인텔 또는 PPC 시스템에서 실행될 코드를 작성하고 정확히 동일한 코드를 사용해야합니다 (예 : 조건부 컴파일 없음).


4
: 완성도를 위해서 여기 (컴파일시) 게이지 엔디 언하려고에 대한 다른 사람의 질문에 대한 링크입니다 stackoverflow.com/questions/280162/...
파이잘 VALI

14
컴파일 타임에 엔디안을 결정하지 않는 이유는 무엇입니까? 런타임에는 변경 될 수 없습니다.
ephemient 2016 년

3
AFAIK, 신뢰할 수 있고 보편적 인 방법은 없습니다. gcc.gnu.org/ml/gcc-help/2007-07/msg00342.html
user48956

답변:


174

유형 punning을 기반으로 한 메소드가 마음에 들지 않습니다. 종종 컴파일러가 경고합니다. 그것이 바로 노조를위한 것입니다!

bool is_big_endian(void)
{
    union {
        uint32_t i;
        char c[4];
    } bint = {0x01020304};

    return bint.c[0] == 1; 
}

원칙은 다른 사람들이 제안한 유형의 경우와 동일하지만 더 명확하며 C99에 따르면 정확합니다. gcc는 이것을 직접 포인터 캐스트와 비교하여 선호합니다.

이것은 컴파일 타임에 엔디안을 수정하는 것보다 훨씬 낫습니다-다중 아키텍처 (예 : Mac OS x의 지방 이진)를 지원하는 OS의 경우 ppc / i386 모두에서 작동하지만 다른 방법으로는 엉망이되기 쉽습니다. .


51
변수 "bint"의 이름을 지정하지 않는 것이 좋습니다 :)
mkb

42
이것이 잘 정의되어 있습니까? C ++에서는 한 번에 한 조합의 멤버 만 활성화 할 수 있습니다. 즉, 하나의 멤버 이름을 사용하여 할당하고 다른 멤버를 사용하여 읽을 수는 없습니다 (레이아웃 호환 구조체에는 예외가 있음)
Faisal Vali

27
@Matt : Google을 조사한 결과, bint는 영어로 알지 못하는 의미가있는 것 같습니다 :)
David Cournapeau

17
나는 이것을 테스트했으며 gcc 4.0.1과 gcc 4.4.1 에서이 함수의 결과는 컴파일 타임에 결정되어 상수로 처리 될 수 있습니다. 이것은이 함수의 결과에만 의존하는 브랜치가 문제의 플랫폼에서 사용되지 않는 경우 컴파일러가 중단됨을 의미합니다. 이것은 많은 htonl 구현에서 사실이 아닙니다.
Omnifarious

6
이 솔루션은 실제로 휴대 할 수 있습니까? 만약에 CHAR_BIT != 8?
zorgit

80

int를 설정하고 비트를 마스킹하여이를 수행 할 수 있지만 가장 쉬운 방법은 내장 네트워크 바이트 변환 ops를 사용하는 것입니다 (네트워크 바이트 순서는 항상 큰 엔디안이므로).

if ( htonl(47) == 47 ) {
  // Big endian
} else {
  // Little endian.
}

비트 피들 링은 더 빠를 수 있지만,이 방법은 간단하고 간단하며 혼란 스러울 수 없습니다.


1
네트워크 변환 ops를 사용하여 모든 것을 빅 엔디안으로 변환하여 Jay가 겪을 수있는 다른 문제를 해결할 수도 있습니다.
Brian

6
@sharptooth-slow는 상대적인 용어이지만, 실제로 속도가 문제라면 프로그램 시작시 한 번만 사용하고 엔디안으로 전역 변수를 설정하십시오.
Eric Petroelje

5
htonl은 또 다른 문제가 있습니다 : 일부 플랫폼 (Windows?)에서는 C 런타임 라이브러리에 적절하지 않지만 네트워크 관련 라이브러리 (소켓 등)에 상주합니다. 라이브러리가 필요하지 않은 경우 하나의 함수에 상당히 방해가됩니다.
David Cournapeau

7
Linux (gcc)에서 htonl은 컴파일 타임에 일정하게 접 히기 때문에이 형식의 표현식은 런타임 오버 헤드가 전혀 없습니다 (즉, 1 또는 0으로 일정하게 접힌 다음 데드 코드 제거는 if의 다른 지점)
bdonlan

2
또한 x86에서 htonl은 특히 인라인 어셈블러를 사용하여 매우 효율적으로 구현 될 수 있으며, 특히 BSWAP작업 을 지원하는 마이크로 아키텍처를 대상으로하는 경우에 유용합니다 .
bdonlan

61

이 기사를 참조 하십시오 :

다음은 컴퓨터 유형을 확인하는 코드입니다.

int num = 1;
if(*(char *)&num == 1)
{
    printf("\nLittle-Endian\n");
}
else
{
    printf("Big-Endian\n");
}

25
int와 char의 길이가 다르므로 거의 항상 그렇지만 보장되지는 않습니다.
David Thornley

10
short int와 char의 크기가 같은 임베디드 시스템에서 일했습니다 ... regular int도 그 크기 (2 바이트)인지 기억이 없습니다.
rmeador 2016 년

2
왜이 답변이 "나, 당신은 뭐하고 있니?"라고 생각하지 않는 유일한 답변입니까? 대부분의 답변이 여기에 있습니다. : o
hanshenrik

2
@Shillard int는 최소한 그보다 커야하지만 표준에는 char에 대한 요구가 적지 않습니다! TI F280x 제품군을 살펴보면 CHAR_BIT가 16이고 sizeof (int) == sizeof (char)임을 알 수 있지만 언급 한 제한은 완전히 유지됩니다 ...
Aconcagua

5
왜 uint8_t와 uint16_t를 사용하지 않습니까?
로드리고

58

std::endianGCC 8+ 또는 Clang 7+와 같은 C ++ 20 컴파일러에 액세스 할 수있는 경우 사용할 수 있습니다 .

참고 : 2019 년 쾰른 회의에서 std::endian시작 <type_traits>되었지만 이전 되었습니다 <bit>. GCC 8, Clang 7, 8 및 9에 포함되어 <type_traits>있고 GCC 9+ 및 Clang 10+에 포함되어 <bit>있습니다.

#include <bit>

if constexpr (std::endian::native == std::endian::big)
{
    // Big endian system
}
else if constexpr (std::endian::native == std::endian::little)
{
    // Little endian system
}
else
{
    // Something else
}

5
모두 C ++ 17 및 20 개의 초안 / 제안에 액세스 할 수 있지만 현재로서는 C ++ 20 컴파일러가 있습니까?
Xeverous

@Xeverous 범위가 지정된 열거 만 필요하므로 대부분의 공급 업체가 stdlib 구현에 이전 변경 사항 중 하나로 추가 할 것으로 생각합니다.
Pharap

@Xeverous GCC 8이 출시되어 지원합니다.
Lyberta

질문에 대한 30 개 이상의 답변 중에서 이것은 완전히 정확한 것으로 보입니다 (적어도 부분적으로는 다른 답변과 함께).
IInspectable

40

이것은 일반적으로 컴파일러에서 사용 가능한 헤더 파일을 사용하여 컴파일 타임에 (특히 성능상의 이유로) 수행되거나 사용자가 직접 생성합니다. 리눅스에는 "/usr/include/endian.h"헤더 파일이 있습니다


8
나는 이것이 더 높은 표를 얻지 못했다는 것을 믿을 수 없다. 엔디안이 컴파일 된 프로그램에서 변경되는 것과 같지 않으므로 런타임 테스트가 필요하지 않습니다.
Dolda2000

@ Dolda2000 잠재적으로 ARM 엔디안 모드를 볼 수 있습니다.
Tyzoid

10
@Tyzoid : 아닙니다. 컴파일 된 프로그램은 프로세서가 어느 쪽이든 가능하더라도 컴파일 된 엔디안 모드에서 항상 실행됩니다.
Dolda2000

16

전처리 기가 기본적으로 정의하는 매크로에 대해 언급 한 사람이 아무도 없습니다. 플랫폼에 따라 다르지만 그들은 자신의 엔디 언 체크를 작성하는 것보다 훨씬 깨끗합니다.

예를 들어; GCC가 정의한 내장 매크로 (X86-64 컴퓨터에서)를 보면 :

:| gcc -dM -E -x c - |grep -i endian
#define __LITTLE_ENDIAN__ 1

PPC 기계에서 나는 얻는다 :

:| gcc -dM -E -x c - |grep -i endian
#define __BIG_ENDIAN__ 1
#define _BIG_ENDIAN 1

( :| gcc -dM -E -x c -매직은 모든 ​​내장 매크로를 인쇄합니다).


7
이 매크로는 전혀 일관성있게 나타나지 않습니다. 예를 들어 Redhat 6 리포지토리의 gcc 4.4.5에서 running echo "\n" | gcc -x c -E -dM - |& grep -i 'endian'은 아무 것도 반환하지 않지만 /usr/sfw/binSolaris의 gcc 3.4.3 은 이러한 행에 따라 정의를 갖습니다. VxWorks Tornado (gcc 2.95) -vs- VxWorks Workbench (gcc 3.4.4)에서 비슷한 문제가 발생했습니다.
Brian Vandenberg

15

흠 ... 컴파일러가 단순히 테스트를 최적화하고 고정 된 결과를 반환 값으로 넣을 것이라는 것을 아무도 모른다는 것이 놀랍습니다. 이것은 위의 모든 코드 예제를 효과적으로 쓸모 없게 만듭니다. 반환되는 유일한 것은 컴파일 타임의 엔디안입니다! 그리고 예, 위의 모든 예제를 테스트했습니다. 다음은 MSVC 9.0 (Visual Studio 2008)의 예입니다.

순수한 C 코드

int32 DNA_GetEndianness(void)
{
    union 
    {
        uint8  c[4];
        uint32 i;
    } u;

    u.i = 0x01020304;

    if (0x04 == u.c[0])
        return DNA_ENDIAN_LITTLE;
    else if (0x01 == u.c[0])
        return DNA_ENDIAN_BIG;
    else
        return DNA_ENDIAN_UNKNOWN;
}

분해

PUBLIC  _DNA_GetEndianness
; Function compile flags: /Ogtpy
; File c:\development\dna\source\libraries\dna\endian.c
;   COMDAT _DNA_GetEndianness
_TEXT   SEGMENT
_DNA_GetEndianness PROC                 ; COMDAT

; 11   :     union 
; 12   :     {
; 13   :         uint8  c[4];
; 14   :         uint32 i;
; 15   :     } u;
; 16   : 
; 17   :     u.i = 1;
; 18   : 
; 19   :     if (1 == u.c[0])
; 20   :         return DNA_ENDIAN_LITTLE;

    mov eax, 1

; 21   :     else if (1 == u.c[3])
; 22   :         return DNA_ENDIAN_BIG;
; 23   :     else
; 24   :        return DNA_ENDIAN_UNKNOWN;
; 25   : }

    ret
_DNA_GetEndianness ENDP
END

아마도이 기능에 대해서만 컴파일 타임 최적화를 해제 할 수는 있지만 모르겠습니다. 그렇지 않으면 이식 가능하지는 않지만 어셈블리에서 하드 코딩 할 수 있습니다. 그리고 심지어 그조차도 최적화 될 수 있습니다. 정말 어리석은 어셈블러가 필요하고 기존의 모든 CPU / 명령어 세트에 대해 동일한 코드를 구현한다고 생각합니다.

또한, 누군가는 런타임 동안 엔디안이 변경되지 않는다고 말했습니다. 잘못된. 바이 엔디안 머신이 있습니다. 그들의 엔디안은 실행 시간에 따라 달라질 수 있습니다. 또한 리틀 엔디안과 빅 엔디안뿐만 아니라 다른 엔디안도 있습니다 (단어).

나는 동시에 코딩을 싫어하고 좋아합니다 ...


11
다른 플랫폼에서 실행하기 위해 다시 컴파일 할 필요가 없습니까?
bobobobo

2
MSVC에서는 잘 작동하지만 모든 상황에서 모든 GCC 버전에 맞는 것은 아닙니다. 따라서 임계 루프 내부의 "런타임 검사"가 컴파일 타임에 올바르게 분기되지 않을 수 있습니다. 100 % 보증은 없습니다.
Cyan

21
빅 엔디안 x86 프로세서는 없습니다. ARM 또는 MIPS와 같은 비 엔디안 프로세서에서 Ubuntu를 실행하더라도 ELF 실행 파일은 항상 빅 (MSB) 또는 리틀 (LSB) 엔디안입니다. Biendian 실행 파일을 만들 수 없으므로 런타임 검사가 필요하지 않습니다.
Fabel

4
이 방법에서 최적화를 끄려면 'volatile union ...'을 사용하십시오. 컴파일러는 'u'를 다른 곳에서 변경할 수 있고 데이터를로드해야한다고 알려줍니다.
mishmashru

1
이 함수가 런타임시 다른 값을 옵티마이 저가 다른 옵티 마이저에 버그가 있음을 암시한다고 계산할 때 계산합니다. 컴파일하는 동안 최적화 프로그램 (프로그램 전체)이 수행 한 명백한 가정에도 불구하고 서로 다른 엔디안의 두 가지 아키텍처에서 이식 가능하게 실행될 수있는 컴파일 된 최적화 된 이진 코드의 예가 있다고 말하고 있습니까? 아키텍처?
Scott

13

int 변수를 선언하십시오.

int variable = 0xFF;

이제 char * 포인터를 사용하여 다양한 부분을 확인하고 해당 부분에 무엇이 있는지 확인하십시오.

char* startPart = reinterpret_cast<char*>( &variable );
char* endPart = reinterpret_cast<char*>( &variable ) + sizeof( int ) - 1;

어느 쪽이 0xFF 바이트를 가리키는 지에 따라 엔디안을 감지 할 수 있습니다. 여기에는 sizeof (int)> sizeof (char)가 필요하지만 논의 된 플랫폼의 경우에는 반드시 사실입니다.


8

자세한 내용은이 코드 프로젝트 기사 Endianness에 대한 기본 개념 을 확인하십시오 .

런타임에 엔디안 유형을 동적으로 테스트하는 방법은 무엇입니까?

컴퓨터 애니메이션 FAQ에 설명 된대로 다음 기능을 사용하여 코드가 Little- 또는 Big-Endian 시스템에서 실행 중인지 확인할 수 있습니다.

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

이 코드는 0001h 값을 16 비트 정수에 할당합니다. 그런 다음 정수 값의 첫 번째 (최하위) 바이트를 가리 키도록 문자 포인터가 할당됩니다. 정수의 첫 번째 바이트가 0x01h이면 시스템은 Little-Endian입니다 (0x01h는 가장 낮거나 가장 중요하지 않은 주소에 있음). 0x00h이면 시스템은 Big-Endian입니다.


6

C ++ 방식은 boost 을 사용 하여 전 처리기 검사 및 캐스트가 철저히 테스트 된 라이브러리 내에서 구획화됩니다.

Predef 라이브러리 (boost / predef.h)는 네 가지 종류의 엔디안을 인식 합니다.

엔디안 라이브러리는 C ++ 표준에 제출 될 예정 및 엔디안에 민감한 데이터에 대한 다양한 조작을 지원했다.

위의 답변에서 언급했듯이 Endianness는 c ++ 20의 일부입니다.


1
참고로, "4 가지 종류의 엔디안"링크가 끊어졌습니다.
Remy Lebeau

고정 및 위키
fuzzyTew

5

PPC와 Intel 프로세서로 이식 된 프레임 워크를 사용하지 않는 한 PPC와 Intel 플랫폼은 완전히 다른 하드웨어 아키텍처, 파이프 라인, 버스 등을 갖기 때문에 조건부 컴파일을 수행해야합니다. 이렇게하면 어셈블리 코드가 완전히 달라집니다. 둘.

엔디안을 찾는 방법은 다음과 같습니다.

short temp = 0x1234;
char* tempChar = (char*)&temp;

tempChar를 0x12 또는 0x34로 설정하면 엔디안을 알 수 있습니다.


3
이것은 정확히 2 바이트가 아닌 short에 의존하므로 보장되지 않습니다.
sharptooth

3
그래도 질문에 주어진 두 아키텍처를 기반으로 꽤 안전한 내기 일 것입니다.
Daemin

8
다른 플랫폼에서 차이가 나지 않도록 미래의 증거를 포함 stdint.h하고 사용 하십시오 int16_t.
Denise Skidmore 2016 년

4

나는 이런 식으로 할 것입니다 :

bool isBigEndian() {
    static unsigned long x(1);
    static bool result(reinterpret_cast<unsigned char*>(&x)[0] == 0);
    return result;
}

이 라인을 따라 계산을 한 번만 수행하는 시간 효율적인 함수를 얻을 수 있습니다.


인라인 할 수 있습니까? 인라인이 정적 변수의 여러 메모리 블록을 유발하는지 확실하지 않음
aah134

4

위에서 언급했듯이 유니온 트릭을 사용하십시오.

그러나 위에서 언급 한 것들에는 거의 문제가 없으며, 대부분의 아키텍처에서 정렬되지 않은 메모리 액세스가 느리게 알려져 있으며, 일부 컴파일러는 단어가 정렬되지 않으면 그러한 상수 술어를 전혀 인식하지 못합니다.

단순한 엔디안 테스트는 지루하기 때문에 호스트 아키텍처와 상관없이 사양에 따라 임의의 정수의 입력 / 출력을 뒤집는 (템플릿) 기능이 있습니다.

#include <stdint.h>

#define BIG_ENDIAN 1
#define LITTLE_ENDIAN 0

template <typename T>
T endian(T w, uint32_t endian)
{
    // this gets optimized out into if (endian == host_endian) return w;
    union { uint64_t quad; uint32_t islittle; } t;
    t.quad = 1;
    if (t.islittle ^ endian) return w;
    T r = 0;

    // decent compilers will unroll this (gcc)
    // or even convert straight into single bswap (clang)
    for (int i = 0; i < sizeof(r); i++) {
        r <<= 8;
        r |= w & 0xff;
        w >>= 8;
    }
    return r;
};

용법:

주어진 엔디안에서 호스트로 변환하려면 다음을 사용하십시오.

host = endian(source, endian_of_source)

호스트 엔디안에서 지정된 엔디안으로 변환하려면 다음을 사용하십시오.

output = endian(hostsource, endian_you_want_to_output)

결과 코드는 clang에서 핸드 어셈블리를 작성하는 것만 큼 빠르며 gcc에서는 속도가 느리지 만 (매 1 바이트마다 &, <<, >>, |), 여전히 괜찮습니다.


4
bool isBigEndian()
{
    static const uint16_t m_endianCheck(0x00ff);
    return ( *((uint8_t*)&m_endianCheck) == 0x0); 
}

1
이것은 동등합니까? #define IS_BIGENDIAN() (*((char*) &((int){ 0x00ff })) == (0x00))
Emanuel

4

를 사용하지 마십시오 union!

C ++에서는 unions 를 통한 유형 제거를 허용하지 않습니다 !
마지막으로 작성된 필드가 아닌 공용체 필드에서 읽는 것은 정의되지 않은 동작입니다 !
많은 컴파일러가 확장 기능을 지원하지만 언어는 보장하지 않습니다.

자세한 내용은이 답변을 참조하십시오.

https://stackoverflow.com/a/11996970


이식성이 보장되는 유효한 답변은 두 가지뿐입니다.

첫 번째 대답은 C ++ 20을 지원하는 시스템에 액세스 할 수있는 경우 헤더 에서
사용 std::endian하는 것 <type_traits>입니다.

(작성 당시 C ++ 20은 아직 출시되지 않았지만 std::endian포함 에 영향을 미치지 않는 한 C ++ 20부터 컴파일 타임에 엔디안을 테스트하는 데 선호되는 방법입니다.)

C ++ 20 이상

constexpr bool is_little_endian = (std::endian::native == std::endian::little);

C ++ 20 이전에는 유일하게 유효한 대답은 정수를 저장 한 다음 punning 유형을 통해 첫 번째 바이트를 검사하는 것입니다. s
의 사용과 달리 union, 이것은 C ++의 타입 시스템에 의해 명백히 허용됩니다.

구현이 정의되어 있기 때문에 최적의 이식성을 위해 static_cast사용해야한다는 것을 기억하는 것도 중요합니다 .
reinterpret_cast

프로그램이 다음 유형 중 하나 이외의 glvalue를 통해 객체의 저장된 값에 액세스하려고하면 동작이 정의되지 않습니다 : ... a char또는 unsigned chartype.

C ++ 11 이상

enum class endianness
{
    little = 0,
    big = 1,
};

inline endianness get_system_endianness()
{
    const int value { 0x01 };
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01) ? endianness::little : endianness::big;
}

C ++ 11 이상 (enum 제외)

inline bool is_system_little_endian()
{
    const int value { 0x01 };
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01);
}

C ++ 98 / C ++ 03

inline bool is_system_little_endian()
{
    const int value = 0x01;
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01);
}

3
union {
    int i;
    char c[sizeof(int)];
} x;
x.i = 1;
if(x.c[0] == 1)
    printf("little-endian\n");
else    printf("big-endian\n");

이것은 또 다른 해결책입니다. Andrew Hare의 솔루션과 유사합니다.


3

테스트를 거치지 않았지만 제 생각에는 이것이 효과가 있습니까? 리틀 엔디안에서는 0x01, 빅 엔디안에서는 0x00이됩니까?

bool runtimeIsLittleEndian(void)
{
 volatile uint16_t i=1;
 return  ((uint8_t*)&i)[0]==0x01;//0x01=little, 0x00=big
}

3

알리다:

초기 게시물이 "컴파일 시간"으로 잘못 선언되었습니다. 현재 C ++ 표준에서는 불가능합니다. constexpr은 함수가 항상 컴파일 타임 계산을 수행한다는 것을 의미하지 않습니다. 수정 해 주신 Richard Hodges에게 감사드립니다.

컴파일 타임, 비 매크로 C ++ 11 constexpr 솔루션 :

union {
  uint16_t s;
  unsigned char c[2];
} constexpr static  d {1};

constexpr bool is_little_endian() {
  return d.c[0] == 1;
}

2
uint8_t보다 unsigned char을 사용한 특별한 이유가 있습니까?
케빈

0 런타임 오버 헤드 ... 마음에 듭니다!
hanshenrik

나는 이것이 타겟이 아닌 빌드 머신의 엔디안을 감지한다고 생각한다.
hutorny

2
C ++에서이 UB가 아닙니까?
rr-

6
이것은 constexpr 문맥에서 합법적이지 않습니다. 직접 초기화되지 않은 조합원은 액세스 할 수 없습니다. 전 처리기 마법없이 컴파일 타임에 엔디안을 합법적으로 감지 할 수있는 방법이 없습니다.
Richard Hodges

2

부스트 엔디안을 찾을 수있는 부스트 헤더 파일과 같은 것을 사용하여 전처리기를 통해이 작업을 수행 할 수도 있습니다


1

엔디안 헤더가 GCC 전용이 아니라면 사용할 수있는 매크로를 제공합니다.

#include "endian.h"
...
if (__BYTE_ORDER == __LITTLE_ENDIAN) { ... }
else if (__BYTE_ORDER == __BIG_ENDIAN) { ... }
else { throw std::runtime_error("Sorry, this version does not support PDP Endian!");
...

이러한 아닌가 __BYTE_ORDER__, __ORDER_LITTLE_ENDIAN__그리고 __ORDER_BIG_ENDIAN__?
Xeverous

1

조건부 컴파일을 원하지 않으면 엔디안 독립 코드를 작성하면됩니다. 다음은 Rob Pike 에서 가져온 예입니다 .

엔디안 독립 방식으로 디스크의 리틀 엔디안에 저장된 정수 읽기 :

i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);

머신 엔디안을 고려하려는 동일한 코드 :

i = *((int*)data);
#ifdef BIG_ENDIAN
/* swap the bytes */
i = ((i&0xFF)<<24) | (((i>>8)&0xFF)<<16) | (((i>>16)&0xFF)<<8) | (((i>>24)&0xFF)<<0);
#endif

좋은 생각이야! 이제 네트워크 소켓을 통해 정수를 알 수없는 장치로 옮깁니다.
Maksym Ganenko

@MaksymGanenko 의견이 없습니다. 아이러니입니까? 나는 하지 직렬화 된 데이터의 엔디안을 지정하지 시사한다. 데이터를 수신하는 시스템의 엔디안에 따라 코드를 작성하지 말 것을 제안합니다.
fjardon

@MaksymGanenko 당신이 downvote하는 경우, 당신은 왜 대답이 틀렸는 지 설명 할 수 있습니다. 잠재적 독자들이 내 답변을 따라 가지 말아야하는 이유를 이해하도록 최소한 도와주십시오.
fjardon


0

이건 어때요?

#include <cstdio>

int main()
{
    unsigned int n = 1;
    char *p = 0;

    p = (char*)&n;
    if (*p == 1)
        std::printf("Little Endian\n");
    else 
        if (*(p + sizeof(int) - 1) == 1)
            std::printf("Big Endian\n");
        else
            std::printf("What the crap?\n");
    return 0;
}

0

다른 C 버전이 있습니다. wicked_cast()C99 공용체 리터럴과 비표준 __typeof__연산자 를 통해 인라인 유형 치기를 호출하는 매크로를 정의합니다 .

#include <limits.h>

#if UCHAR_MAX == UINT_MAX
#error endianness irrelevant as sizeof(int) == 1
#endif

#define wicked_cast(TYPE, VALUE) \
    (((union { __typeof__(VALUE) src; TYPE dest; }){ .src = VALUE }).dest)

_Bool is_little_endian(void)
{
    return wicked_cast(unsigned char, 1u);
}

정수가 1 바이트 값인 경우 엔디안은 의미가 없으며 컴파일 타임 오류가 발생합니다.


0

C 컴파일러 (내가 아는 모든 사람)가 엔디안 작동시키는 방식 은 컴파일 타임에 결정되어야합니다. ARM och MIPS와 같은 비 엔디안 프로세서의 경우에도 컴파일시 엔디안을 선택해야합니다. 또한 엔디안은 실행 파일 (예 : ELF)의 모든 공통 파일 형식으로 정의됩니다. 바이너리 코드의 바이너리 코드를 만들 수도 있지만 (일부 ARM 서버의 경우에는 어쩌면?) 아마도 어셈블리에서 수행되어야합니다.


-1

Coriiander가 지적한 것처럼 여기에서 이러한 코드의 대부분은 전부는 아니지만 컴파일 타임에 최적화되므로 생성 된 바이너리는 런타임에 "엔디안"을 확인하지 않습니다.

주어진 실행 파일이 두 개의 다른 바이트 순서로 실행되어서는 안된다는 것이 관찰되었지만, 항상 그런 경우인지는 알지 못하며 컴파일 타임에 확인하는 것이 해킹처럼 보입니다. 그래서 나는이 기능을 코딩했다 :

#include <stdint.h>

int* _BE = 0;

int is_big_endian() {
    if (_BE == 0) {
        uint16_t* teste = (uint16_t*)malloc(4);
        *teste = (*teste & 0x01FE) | 0x0100;
        uint8_t teste2 = ((uint8_t*) teste)[0];
        free(teste);
        _BE = (int*)malloc(sizeof(int));
        *_BE = (0x01 == teste2);
    }
    return *_BE;
}

MinGW는 여기에서 다른 코드를 최적화하더라도이 코드를 최적화 할 수 없었습니다. 더 작은 바이트 메모리에 할당 된 "임의의"값을 그대로 (비트 중 7 비트 이상) 남겨두기 때문에 컴파일러는 임의의 값이 무엇인지 알 수 없으며 최적화하지 않습니다. 기능을 멀리합니다.

또한 검사가 한 번만 수행되고 다음 테스트를 위해 반환 값이 저장되도록 함수를 코딩했습니다.


2 바이트 값에서 작동하도록 4 바이트를 할당하는 이유는 무엇입니까? 왜 불확정 한 값을 0x7FE? 왜 malloc()전혀 사용하지 않습니까? 그것은 낭비입니다. 그리고 _BE(작지만) 메모리 누수가 발생하고 경쟁 조건이 발생하기 때문에 결과를 동적으로 캐싱하는 이점은 문제가되지 않습니다. : 나는 더 많은 대신이 같은 일을 할 것입니다 static const uint16_t teste = 1; int is_little_endian() { return (0x01 == ((uint8_t*)&teste)[0]); } int is_big_endian() { return (0x01 == ((uint8_t*)&teste)[1]); }런타임에 수행 할 수있는 간단하고 효과적인, 그리고 훨씬 적은 작업.
Remy Lebeau

@RemyLebeau, 내 대답의 요점은 컴파일러가 최적화하지 않은 코드를 생성하는 것이 었습니다. 물론 코드는 훨씬 간단하지만 최적화 기능을 켜면 컴파일 후에도 일정한 부울 값이됩니다. 내 대답에 언급했듯이 실제로 동일한 실행 파일이 두 바이트 순서에서 모두 실행되는 방식으로 C 코드를 컴파일 할 수있는 방법이 있는지 알지 못하며 런타임에 확인할 수 있는지 궁금합니다. 최적화가 켜져 있음에도 불구하고.
Tex Killer

@TexKiller 그렇다면 왜 단순히 코드 최적화를 비활성화하지 않습니까? volatile또는 #pragma등을 사용하여
Remy Lebeau

@RemyLebeau, 나는 당시에 그 키워드를 몰랐으며, 내가 알고있는 것으로 컴파일러 최적화를 막기 위해 약간의 도전으로 그것을 보았습니다.
Tex Killer

-1

그것을 결정하는 빠르고 표준적인 방법은 없지만 다음과 같이 출력합니다.

#include <stdio.h> 
int main()  
{ 
   unsigned int i = 1; 
   char *c = (char*)&i; 
   if (*c)     
       printf("Little endian"); 
   else
       printf("Big endian"); 
   getchar(); 
   return 0; 
} 

-1

엔디안 -C 레벨 코드 그림을 참조하십시오 .

// assuming target architecture is 32-bit = 4-Bytes
enum ENDIANNESS{ LITTLEENDIAN , BIGENDIAN , UNHANDLE };


ENDIANNESS CheckArchEndianalityV1( void )
{
    int Endian = 0x00000001; // assuming target architecture is 32-bit    

    // as Endian = 0x00000001 so MSB (Most Significant Byte) = 0x00 and LSB (Least     Significant Byte) = 0x01
    // casting down to a single byte value LSB discarding higher bytes    

    return (*(char *) &Endian == 0x01) ? LITTLEENDIAN : BIGENDIAN;
} 

-2

나는 컴퓨터 시스템 : 프로그래머의 관점 교과서를 살펴 보았고 C 프로그램이 어떤 엔디안인지 결정하는 데 문제가 있습니다.

포인터의 기능을 사용하여 다음과 같이했습니다.

#include <stdio.h>

int main(void){
    int i=1;
    unsigned char* ii = &i;

    printf("This computer is %s endian.\n", ((ii[0]==1) ? "little" : "big"));
    return 0;
}

는 AS INT는 4 바이트를 차지하며 문자는 단 1 바이트를 차지합니다. 우리는 사용할 수있는 문자 포인터를 받는 점에 INT 컴퓨터가 리틀 엔디안의 경우에 따라서 값 1로 문자 있다는 문자 포인터 가 가리키는 값 1이고, 그렇지 않으면 그 값은 0이어야한다.


이것은 int32t를 사용하여 향상됩니다.
shuttle87

1
^ 당신이 nitpick하고 싶다면, 여기서 가장 좋은 것은 int16_fast_t입니다. 및 INT는 기본적으로 INT8 인 아치하지 작동 Archimedes520의 현재 코드 @;) (즉)하지만, 처음에 C 표준에 갈 수도
hanshenrik
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.