Büyük-endian mı yoksa küçük-endian mimarisi mi olduğunuzu tespit etmenin programlı bir yolu var mı? Bir Intel veya PPC sistemi üzerinde çalışacak ve tam olarak aynı kodu (yani koşullu derleme) kullanacak kod yazmak gerekir.
Büyük-endian mı yoksa küçük-endian mimarisi mi olduğunuzu tespit etmenin programlı bir yolu var mı? Bir Intel veya PPC sistemi üzerinde çalışacak ve tam olarak aynı kodu (yani koşullu derleme) kullanacak kod yazmak gerekir.
Yanıtlar:
Tip çiftçiliği temel alan yöntemi sevmiyorum - genellikle derleyici tarafından uyarılır. Sendikalar tam da bunun için!
bool is_big_endian(void)
{
union {
uint32_t i;
char c[4];
} bint = {0x01020304};
return bint.c[0] == 1;
}
İlke, başkaları tarafından önerildiği gibi tip durumuna eşdeğerdir, ancak bu daha açıktır - ve C99'a göre, doğru olduğu garanti edilir. gcc bunu doğrudan işaretçi kadrosuna kıyasla tercih eder.
Bu aynı zamanda endianiteyi derleme zamanında düzeltmekten çok daha iyidir - çoklu mimariyi destekleyen OS için (örneğin Mac os x'de yağ ikili), bu hem ppc / i386 için işe yarayacaktır, oysa başka şeyleri karıştırmak çok kolaydır .
CHAR_BIT != 8
?
Bir int ayarlayıp bitleri maskeleyerek yapabilirsiniz, ancak muhtemelen en kolay yol sadece yerleşik ağ bayt dönüştürme işlemlerini kullanmaktır (ağ bayt sırası her zaman büyük endian olduğundan).
if ( htonl(47) == 47 ) {
// Big endian
} else {
// Little endian.
}
Biraz uğraşmak daha hızlı olabilir, ancak bu şekilde basit, anlaşılır ve dağılması oldukça imkansızdır.
BSWAP
işlem desteğine sahip bir mikro mimariyi hedeflerseniz, satır içi birleştirici kullanılarak çok verimli bir şekilde uygulanabilir (ve Linux / gcc'de) .
Lütfen bu makaleye bakın :
Makinenizin türünü belirlemek için bazı kodlar
int num = 1; if(*(char *)&num == 1) { printf("\nLittle-Endian\n"); } else { printf("Big-Endian\n"); }
std::endian
GCC 8+ veya Clang 7+ gibi C ++ 20 derleyicisine erişiminiz varsa kullanabilirsiniz .
Not: std::endian
başlayan <type_traits>
ancak taşındı için <bit>
2019 Köln toplantısında. GCC 8, Clang 7, 8 ve 9 <type_traits>
içeri girerken GCC 9+ ve Clang 10+ içeri girmiştir <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
}
Bu normal olarak derleyici zamanında (özellikle performans nedeniyle) derleyiciden elde edilebilen başlık dosyaları kullanılarak veya kendinizinkini oluşturarak yapılır. Linux'ta "/usr/include/endian.h" başlık dosyasına sahipsiniz
Ön işlemcinin varsayılan olarak tanımladığı makrolardan kimsenin bahsetmediğine şaşırdım. Bunlar platformunuza bağlı olarak değişse de; kendi endian çekinizi yazmaktan çok daha temizler.
Örneğin; GCC'nin tanımladığı yerleşik makrolara bakarsak (bir X86-64 makinesinde):
:| gcc -dM -E -x c - |grep -i endian
#define __LITTLE_ENDIAN__ 1
Bir PPC makinesinde:
:| gcc -dM -E -x c - |grep -i endian
#define __BIG_ENDIAN__ 1
#define _BIG_ENDIAN 1
( :| gcc -dM -E -x c -
Sihir tüm yerleşik makroları yazdırır).
echo "\n" | gcc -x c -E -dM - |& grep -i 'endian'
hiçbir şey döndürmezken, /usr/sfw/bin
Solaris'deki gcc 3.4.3 ( yine de) bu hatlar boyunca bir tanıma sahiptir. VxWorks Tornado (gcc 2.95) -vs- VxWorks Workbench (gcc 3.4.4) ile ilgili benzer sorunlar gördüm.
Ehm ... Kimsenin derleyicinin testi basitçe optimize edeceğini ve geri dönüş değeri olarak sabit bir sonuç getireceğini fark etmemesi beni şaşırtıyor. Bu, yukarıdaki tüm kod örneklerini etkili bir şekilde işe yaramaz hale getirir. Döndürülecek tek şey derleme zamanında endianness! Ve evet, yukarıdaki örneklerin tümünü test ettim. MSVC 9.0 (Visual Studio 2008) ile ilgili bir örnek.
Saf C kodu
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;
}
sökme
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
Belki de sadece bu işlev için herhangi bir derleme zamanı optimizasyonunu kapatmak mümkündür, ama bilmiyorum. Aksi takdirde, taşınabilir olmasa da, montajda sabit kodlamak mümkün olabilir. Ve o zaman bile bu optimize edilebilir. Bana gerçekten berbat bir montajcıya ihtiyacım var, tüm mevcut CPU / komut setleri için aynı kodu uygular ve iyi .... boş ver.
Ayrıca, burada birisi endiannessin çalışma zamanı boyunca değişmediğini söyledi. YANLIŞ. Dışarıda bi-endian makineleri var. Endianiteleri yürütme sırasında değişebilir. AYRICA, sadece Küçük Endian ve Big Endian değil, aynı zamanda diğer endiannesslar da (ne kelime).
Kodlamayı aynı anda sevmiyorum ve seviyorum ...
Bir int değişkeni bildirin:
int variable = 0xFF;
Şimdi çeşitli kısımlarında char * işaretçileri kullanın ve bu parçalarda ne olduğunu kontrol edin.
char* startPart = reinterpret_cast<char*>( &variable );
char* endPart = reinterpret_cast<char*>( &variable ) + sizeof( int ) - 1;
Hangisinin 0xFF baytını gösterdiğine bağlı olarak, endianlığı tespit edebilirsiniz. Bu sizeof (int)> sizeof (char) gerektirir, ancak tartışılan platformlar için kesinlikle doğrudur.
Daha fazla ayrıntı için Endianness ile ilgili temel kavramlar başlıklı bu kod projesine göz atmak isteyebilirsiniz :
Çalışma zamanında Endian tipi için dinamik olarak nasıl test edilir?
Bilgisayar Animasyonu SSS bölümünde açıklandığı gibi, kodunuzun Little- veya Big-Endian sisteminde çalışıp çalışmadığını görmek için aşağıdaki işlevi kullanabilirsiniz: Daralt
#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);
}
Bu kod, 16 bit tam sayıya 0001h değerini atar. Daha sonra, tamsayı değerinin ilk (en az anlamlı) baytını gösterecek bir karakter işaretçisi atanır. Tamsayının ilk baytı 0x01h ise, sistem Little-Endian olur (0x01h en düşük veya en az anlamlı adrestir). 0x00h ise sistem Big-Endian'tır.
C ++ yolu, önişlemci kontrolleri ve yayınlarının çok iyi test edilmiş kitaplıkların içinde bölümlere ayrıldığı destek kullanmaktır .
Predef Kütüphanesi (boost / predef.h) dört farklı endianlığı tanır .
Endian Kütüphane C ++ standardı sunulmak üzere planlanan ve endian duyarlı veri işlemleri çok çeşitli destekler edildi.
Yukarıdaki cevaplarda belirtildiği gibi, Endianness c ++ 20'nin bir parçası olacaktır.
PPC ve Intel işlemcilere taşınan bir çerçeve kullanmadığınız sürece, PPC ve Intel platformları tamamen farklı donanım mimarilerine, boru hatlarına, veri yollarına vb. Sahip olduğundan koşullu derlemeler yapmanız gerekecektir. iki.
Endianliği bulmak için aşağıdakileri yapın:
short temp = 0x1234;
char* tempChar = (char*)&temp;
TempChar'ı endiannessi bileceğiniz 0x12 veya 0x34 olacaksınız.
stdint.h
ve kullanın int16_t
.
Böyle bir şey yapardım:
bool isBigEndian() {
static unsigned long x(1);
static bool result(reinterpret_cast<unsigned char*>(&x)[0] == 0);
return result;
}
Bu çizgiler boyunca, hesaplamayı yalnızca bir kez yapan zamandan tasarruf sağlayan bir fonksiyon elde edersiniz.
Yukarıda belirtildiği gibi birleşim hileleri kullanın.
Bununla birlikte, yukarıda tavsiye edilenlerle ilgili birkaç sorun vardır, en önemlisi, hizalanmamış bellek erişiminin çoğu mimaride kötü bir şekilde yavaş olmasıdır ve bazı derleyiciler, kelime hizalanmadığı sürece, bu tür sabit tahminleri bile tanımayacaktır.
Sadece endian testi sıkıcı olduğu için, ana bilgisayar mimarisine bakılmaksızın isteğe bağlı tamsayı giriş / çıkışını özelliğinize göre çeviren (şablon) işlevi.
#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;
};
Kullanımı:
Verilen endiandan ana bilgisayara dönüştürmek için şunu kullanın:
host = endian(source, endian_of_source)
Host endian'dan verilen endian'a dönüştürmek için şunu kullanın:
output = endian(hostsource, endian_you_want_to_output)
Ortaya çıkan kod clang üzerinde el montajı yazmak kadar hızlıdır, gcc'de biraz daha yavaştır (unrolled &, <<, >>, | her bayt için) ama yine de iyi.
bool isBigEndian()
{
static const uint16_t m_endianCheck(0x00ff);
return ( *((uint8_t*)&m_endianCheck) == 0x0);
}
#define IS_BIGENDIAN() (*((char*) &((int){ 0x00ff })) == (0x00))
Kullanmayın union
!
C ++, union
s aracılığıyla punning tipine izin vermez !
En son yazılan alan olmayan bir birlik alanından okumak tanımlanmamış davranıştır !
Birçok derleyici bunu bir uzantı olarak yapmayı destekler, ancak dil hiçbir garanti vermez.
Daha fazla ayrıntı için bu cevaba bakınız:
https://stackoverflow.com/a/11996970
Taşınabilir olduğu garanti edilen yalnızca iki geçerli cevap vardır.
Eğer bir sisteme erişimi varsa ilk cevap, destekler C ++ 20, yani
kullanmaktır std::endian
gelen <type_traits>
başlığındaki.
(Yazma sırasında, C ++ 20 henüz yayınlanmamıştır, ancak bir şeyin std::endian
dahil edilmesini etkileyecek bir şey olmadıkça , C ++ 20'den itibaren derleme zamanında endianlığı test etmek için tercih edilen yol bu olacaktır.)
constexpr bool is_little_endian = (std::endian::native == std::endian::little);
C ++ 20'den önce, tek geçerli cevap bir tamsayıyı saklamak ve daha sonra ilk baytını punning tipi aracılığıyla incelemektir. S
kullanımından farklı olarak union
, bu C ++ 'ın tip sistemi tarafından açıkça izin verilir.
Optimum taşınabilirlik için hatırlamak da önemlidir static_cast
, kullanılması gereken
çünkü reinterpret_cast
uygulama tanımlanır.
Bir program, bir nesnenin depolanan değerine aşağıdaki türlerden birinden farklı bir değerle erişmeye çalışırsa, davranış tanımsızdır: ... a
char
veyaunsigned 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);
}
denenmemiş, ama bence bu işe yaramalı mı? çünkü küçük endianda 0x01, büyük endianda 0x00 olur?
bool runtimeIsLittleEndian(void)
{
volatile uint16_t i=1;
return ((uint8_t*)&i)[0]==0x01;//0x01=little, 0x00=big
}
Bildirmek:
derleme zamanı, makro olmayan, C ++ 11 constexpr çözümü:
union {
uint16_t s;
unsigned char c[2];
} constexpr static d {1};
constexpr bool is_little_endian() {
return d.c[0] == 1;
}
Bunu ayrıca ön işlemciden boost endian gibi bulunabilecek boost header dosyası gibi bir şey kullanarak da yapabilirsiniz.
Endian üstbilgisi yalnızca GCC değilse, kullanabileceğiniz makrolar sağlar.
#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__
ve __ORDER_BIG_ENDIAN__
?
Koşullu derleme istemiyorsanız, endian bağımsız kodunu yazabilirsiniz. İşte bir örnek ( Rob Pike'tan alınmıştır ):
Diskte küçük endian'da saklanan bir tamsayıyı endiandan bağımsız olarak okuma:
i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);
Aynı kod, makine endianitesini dikkate almaya çalışıyor:
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
int i=1;
char *c=(char*)&i;
bool littleendian=c;
İşte başka bir C versiyonu. wicked_cast()
C99 birleşim değişmezleri ve standart olmayan __typeof__
operatör aracılığıyla satır içi tip çiftçilik için çağrılan bir makro tanımlar .
#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);
}
Tamsayılar tek baytlık değerler ise, endianness hiçbir anlam ifade etmez ve bir derleme zamanı hatası üretilir.
Yollu C derleyicileri (en herkese Bildiğim) endianness'ın çalışmak olmuştur derleme sırasında karar verilecek. Biendian işlemciler için bile (ARM och MIPS gibi) derleme zamanında endianlığı seçmelisiniz. Ayrıca endianness, yürütülebilir dosyalar (ELF gibi) için tüm yaygın dosya formatlarında tanımlanır. Her ne kadar bir ikili biandian kod bloğu oluşturmak mümkün olsa da (bazı ARM sunucusu istismar için belki?) Muhtemelen montajda yapılmalıdır.
Coriiander tarafından belirtildiği gibi, bu kodların çoğu (hepsi değilse de) derleme zamanında optimize edilecek, bu nedenle oluşturulan ikili dosyalar çalışma zamanında "endianness" i kontrol etmeyecektir.
Belirli bir yürütülebilir dosyanın iki farklı bayt düzeninde çalıştırılmaması gerektiği gözlendi, ancak her zaman böyle olup olmadığı hakkında hiçbir fikrim yok ve derleme zamanında kontrol etmek bana bir hack gibi görünüyor. Bu işlevi kodladım:
#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, buradaki diğer kodları optimize etmesine rağmen bu kodu optimize edemedi. Bunun nedeni, daha küçük bayt belleğinde olduğu gibi (bitlerinden en az 7 tanesi) ayrılmış olan "rastgele" değeri bıraktığımdan dolayı, derleyicinin bu rastgele değerin ne olduğunu bilemeyeceği ve optimize etmediği için işlev uzak.
Ayrıca işlevi kodladım, böylece kontrol sadece bir kez yapılır ve dönüş değeri sonraki testler için saklanır.
0x7FE
? Neden kullanılmalı malloc()
? bu israftır. Ve _BE
(küçük de olsa) bir bellek sızıntısı ve gerçekleşmeyi bekleyen bir yarış koşulu, sonucu dinamik olarak önbelleğe almanın faydaları sorun yaratmaya değmez. Bunun yerine böyle bir şey yapardım: 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]); }
Basit ve etkili ve çalışma zamanında gerçekleştirmek için çok daha az iş.
volatile
, veya #pragma
vb.
Bkz endianness'ın C-Seviye Kod illüstrasyon -.
// 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;
}
Ders kitabından geçiyordum: Bilgisayar Sistemi: bir programcının bakış açısı ve C programının bu endianın hangisi olduğunu belirlemek için bir sorun var.
İşaretçinin özelliğini aşağıdaki gibi yapmak için kullandım:
#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;
}
Gibi int 4 bayt kaplıyor ve karakter yalnızca 1 bayt kaplıyor. Bir kullanabilir karakter işaretçi için noktasına int bilgisayar küçük sonlu, eğer Böylece 1 değeri ile karakter bu karakter işaretçi işaret ettiği değeri 1 ise, aksi halde, değeri 0 olmalıdır.