빅 엔디안 또는 리틀 엔디안 아키텍처인지 여부를 감지하는 프로그래밍 방식이 있습니까? 인텔 또는 PPC 시스템에서 실행될 코드를 작성하고 정확히 동일한 코드를 사용해야합니다 (예 : 조건부 컴파일 없음).
빅 엔디안 또는 리틀 엔디안 아키텍처인지 여부를 감지하는 프로그래밍 방식이 있습니까? 인텔 또는 PPC 시스템에서 실행될 코드를 작성하고 정확히 동일한 코드를 사용해야합니다 (예 : 조건부 컴파일 없음).
답변:
유형 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 모두에서 작동하지만 다른 방법으로는 엉망이되기 쉽습니다. .
CHAR_BIT != 8
?
int를 설정하고 비트를 마스킹하여이를 수행 할 수 있지만 가장 쉬운 방법은 내장 네트워크 바이트 변환 ops를 사용하는 것입니다 (네트워크 바이트 순서는 항상 큰 엔디안이므로).
if ( htonl(47) == 47 ) {
// Big endian
} else {
// Little endian.
}
비트 피들 링은 더 빠를 수 있지만,이 방법은 간단하고 간단하며 혼란 스러울 수 없습니다.
BSWAP
작업 을 지원하는 마이크로 아키텍처를 대상으로하는 경우에 유용합니다 .
다음은 컴퓨터 유형을 확인하는 코드입니다.
int num = 1; if(*(char *)&num == 1) { printf("\nLittle-Endian\n"); } else { printf("Big-Endian\n"); }
std::endian
GCC 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
}
이것은 일반적으로 컴파일러에서 사용 가능한 헤더 파일을 사용하여 컴파일 타임에 (특히 성능상의 이유로) 수행되거나 사용자가 직접 생성합니다. 리눅스에는 "/usr/include/endian.h"헤더 파일이 있습니다
전처리 기가 기본적으로 정의하는 매크로에 대해 언급 한 사람이 아무도 없습니다. 플랫폼에 따라 다르지만 그들은 자신의 엔디 언 체크를 작성하는 것보다 훨씬 깨끗합니다.
예를 들어; 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 -
매직은 모든 내장 매크로를 인쇄합니다).
echo "\n" | gcc -x c -E -dM - |& grep -i 'endian'
은 아무 것도 반환하지 않지만 /usr/sfw/bin
Solaris의 gcc 3.4.3 은 이러한 행에 따라 정의를 갖습니다. VxWorks Tornado (gcc 2.95) -vs- VxWorks Workbench (gcc 3.4.4)에서 비슷한 문제가 발생했습니다.
흠 ... 컴파일러가 단순히 테스트를 최적화하고 고정 된 결과를 반환 값으로 넣을 것이라는 것을 아무도 모른다는 것이 놀랍습니다. 이것은 위의 모든 코드 예제를 효과적으로 쓸모 없게 만듭니다. 반환되는 유일한 것은 컴파일 타임의 엔디안입니다! 그리고 예, 위의 모든 예제를 테스트했습니다. 다음은 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 / 명령어 세트에 대해 동일한 코드를 구현한다고 생각합니다.
또한, 누군가는 런타임 동안 엔디안이 변경되지 않는다고 말했습니다. 잘못된. 바이 엔디안 머신이 있습니다. 그들의 엔디안은 실행 시간에 따라 달라질 수 있습니다. 또한 리틀 엔디안과 빅 엔디안뿐만 아니라 다른 엔디안도 있습니다 (단어).
나는 동시에 코딩을 싫어하고 좋아합니다 ...
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)가 필요하지만 논의 된 플랫폼의 경우에는 반드시 사실입니다.
자세한 내용은이 코드 프로젝트 기사 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입니다.
C ++ 방식은 boost 을 사용 하여 전 처리기 검사 및 캐스트가 철저히 테스트 된 라이브러리 내에서 구획화됩니다.
Predef 라이브러리 (boost / predef.h)는 네 가지 종류의 엔디안을 인식 합니다.
엔디안 라이브러리는 C ++ 표준에 제출 될 예정 및 엔디안에 민감한 데이터에 대한 다양한 조작을 지원했다.
위의 답변에서 언급했듯이 Endianness는 c ++ 20의 일부입니다.
PPC와 Intel 프로세서로 이식 된 프레임 워크를 사용하지 않는 한 PPC와 Intel 플랫폼은 완전히 다른 하드웨어 아키텍처, 파이프 라인, 버스 등을 갖기 때문에 조건부 컴파일을 수행해야합니다. 이렇게하면 어셈블리 코드가 완전히 달라집니다. 둘.
엔디안을 찾는 방법은 다음과 같습니다.
short temp = 0x1234;
char* tempChar = (char*)&temp;
tempChar를 0x12 또는 0x34로 설정하면 엔디안을 알 수 있습니다.
stdint.h
하고 사용 하십시오 int16_t
.
위에서 언급했듯이 유니온 트릭을 사용하십시오.
그러나 위에서 언급 한 것들에는 거의 문제가 없으며, 대부분의 아키텍처에서 정렬되지 않은 메모리 액세스가 느리게 알려져 있으며, 일부 컴파일러는 단어가 정렬되지 않으면 그러한 상수 술어를 전혀 인식하지 못합니다.
단순한 엔디안 테스트는 지루하기 때문에 호스트 아키텍처와 상관없이 사양에 따라 임의의 정수의 입력 / 출력을 뒤집는 (템플릿) 기능이 있습니다.
#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 바이트마다 &, <<, >>, |), 여전히 괜찮습니다.
를 사용하지 마십시오 union
!
C ++에서는 union
s 를 통한 유형 제거를 허용하지 않습니다 !
마지막으로 작성된 필드가 아닌 공용체 필드에서 읽는 것은 정의되지 않은 동작입니다 !
많은 컴파일러가 확장 기능을 지원하지만 언어는 보장하지 않습니다.
자세한 내용은이 답변을 참조하십시오.
https://stackoverflow.com/a/11996970
이식성이 보장되는 유효한 답변은 두 가지뿐입니다.
첫 번째 대답은 C ++ 20을 지원하는 시스템에 액세스 할 수있는 경우 헤더 에서
사용 std::endian
하는 것 <type_traits>
입니다.
(작성 당시 C ++ 20은 아직 출시되지 않았지만 std::endian
포함 에 영향을 미치지 않는 한 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 char
type.
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;
}
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);
}
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 ++ 11 constexpr 솔루션 :
union {
uint16_t s;
unsigned char c[2];
} constexpr static d {1};
constexpr bool is_little_endian() {
return d.c[0] == 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__
?
조건부 컴파일을 원하지 않으면 엔디안 독립 코드를 작성하면됩니다. 다음은 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
다른 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 바이트 값인 경우 엔디안은 의미가 없으며 컴파일 타임 오류가 발생합니다.
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 비트 이상) 남겨두기 때문에 컴파일러는 임의의 값이 무엇인지 알 수 없으며 최적화하지 않습니다. 기능을 멀리합니다.
또한 검사가 한 번만 수행되고 다음 테스트를 위해 반환 값이 저장되도록 함수를 코딩했습니다.
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]); }
런타임에 수행 할 수있는 간단하고 효과적인, 그리고 훨씬 적은 작업.
volatile
또는 #pragma
등을 사용하여
엔디안 -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;
}
나는 컴퓨터 시스템 : 프로그래머의 관점 교과서를 살펴 보았고 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이어야한다.