Bir C ++ programında endianizmin programlı olarak algılanması


211

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.


4
Tamlık uğruna, başka birinin endiannessi ölçmeye çalışmayla ilgili sorusuna bir bağlantı (derleme zamanında): stackoverflow.com/questions/280162/…
Faisal Vali

14
Derleme zamanında endianlığı neden belirlemiyorsunuz? Çalışma zamanında değişemez.
ephemient

3
AFAIK, bunu yapmanın güvenilir ve evrensel bir yolu yok. gcc.gnu.org/ml/gcc-help/2007-07/msg00342.html
user48956

Yanıtlar:


174

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 .


51
Değişken "bint" adını
vermenizi önermiyorum

42
bunun iyi tanımlandığından emin misin? C ++ 'da birliğin yalnızca bir üyesi aynı anda etkin olabilir - yani bir üye adı kullanarak atayamaz ve diğerini kullanarak okuyamazsınız (her ne kadar düzen uyumlu yapılar için bir istisna olsa da)
Faysal Vali

27
@Matt: Google'a baktım ve bint'in İngilizce'de farkında olmadığım bir anlamı var gibi görünüyor :)
David Cournapeau

17
Bunu test ettim ve hem gcc 4.0.1 hem de gcc 4.4.1'de bu işlevin sonucu derleme zamanında belirlenebilir ve sabit olarak değerlendirilebilir. Bu, yalnızca bu işlevin sonucuna bağlı olan dallar ve söz konusu platformda asla alınmayacaksa derleyicinin düşeceği anlamına gelir. Bu pek çok htonl uygulaması için doğru değildir.
şeye kadir

6
Bu çözüm gerçekten taşınabilir mi? Ya eğer CHAR_BIT != 8?
zorgit

80

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.


1
Ağ dönüştürme opları da her şeyi büyük endian'a dönüştürmek için kullanılabilir, böylece Jay'in karşılaşabileceği diğer sorunları çözebilirsiniz.
Brian

6
@sharptooth - yavaş göreceli bir terimdir, ancak evet, hız gerçekten bir sorunsa, bunu programın başında bir kez kullanın ve endianness ile küresel bir değişken ayarlayın.
Eric Petroelje

5
htonl'un başka bir sorunu daha vardır: bazı platformlarda (windows?), C çalışma zamanı kütüphanesinde uygun değildir, ancak ek olarak ağ ile ilgili kütüphanelerde (soket vb.) bulunur. Aksi takdirde kütüphaneye ihtiyacınız yoksa bu sadece bir işlev için oldukça engeldir.
David Cournapeau

7
Linux'ta (gcc), htonl'un derleme zamanında sabit katlanmaya tabi olduğunu unutmayın, bu nedenle bu formun bir ifadesinin çalışma zamanı ek yükü yoktur (yani 1 veya 0'a sabit olarak katlanır ve sonra ölü kod ortadan kaldırılması if diğer dalı)
bdonlan

2
Ayrıca, x86 htonl, özellikle BSWAPiş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) .
bdonlan

61

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");
}

25
Bunun int ve char'ın farklı uzunluklara bağlı olduğunu ve neredeyse her zaman böyle olduğunu ancak garanti edilmediğini unutmayın.
David Thornley

10
Kısa int ve char aynı boyutta gömülü sistemler üzerinde çalıştım ... Düzenli int de bu boyut (2 bayt) olup olmadığını hatırlayamıyorum.
rmeador

2
neden bu cevap hemen hemen bana "ahbap, ne yapıyorsun?" diye düşünmeyen SADECE YANIT, buradaki cevapların çoğunun durumu: o
hanshenrik

2
@Shillard int en azından bu kadar büyük olmalı, ancak char'ın daha azıyla kısıtlanması için standartta bir gereklilik yoktur! TI F280x ailesine bir göz attıysanız, bahsettiğiniz sınırlar kesinlikle iyi korunurken CHAR_BIT'in 16 ve sizeof (int) == sizeof (char) olduğunu keşfedeceksiniz ...
Aconcagua

5
Neden uint8_t ve uint16_t kullanılmıyor?
Rodrigo

58

std::endianGCC 8+ veya Clang 7+ gibi C ++ 20 derleyicisine erişiminiz varsa kullanabilirsiniz .

Not: std::endianbaş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
}

5
Herkes olarak C ++ 17 ve 20 taslaklara / tekliflere erişimim var, ancak şu andan itibaren herhangi bir C ++ 20 derleyicisi var mı?
Xeverous

@Xeverous Sadece kapsamlı numaralandırma gerektirir, bu yüzden çoğu satıcının önceki değişikliklerinden biri olarak stdlib uygulamalarına ekleyeceğinden şüpheleniyorum.
Pharap

@Xeverous GCC 8 yayınlandı ve destekledi.
Lyberta

Sorunun 30'dan fazla cevabından, bu tamamen doğru gibi görünüyor, bu tamamen doğru (en azından kısmen doğru olan başka bir cevapla).
Ocak'ta

40

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


8
Buna daha yüksek oy verilmediğine inanamıyorum. Derlenmiş bir program altında endianness değişecek gibi değil, bu yüzden asla bir çalışma zamanı testine gerek yoktur.
Dolda2000

@ Dolda2000 Potansiyel olarak ARM endian modlarını görebilir.
Tyzoid

10
@Tyzoid: Hayır, derlenmiş bir program, işlemci her ikisini de yapabiliyor olsa bile, her zaman derlendiği endian modu altında çalışır.
Dolda2000

16

Ö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).


7
Bu makrolar hiç tutarlı görünmüyor. Örneğin, Redhat 6 repo'daki gcc 4.4.5'te çalışan echo "\n" | gcc -x c -E -dM - |& grep -i 'endian'hiçbir şey döndürmezken, /usr/sfw/binSolaris'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.
Brian Vandenberg

15

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 ...


11
Yine de farklı bir platformda çalıştırmak için yeniden derlemenize gerek yok mu?
bobobobo

2
MSVC için iyi çalışmasına rağmen, her koşulda tüm GCC sürümü için geçerli değildir. Bu nedenle, kritik bir döngü içindeki bir "çalışma zamanı kontrolü" derleme zamanında doğru dallanmamış olabilir veya olmayabilir. % 100 garanti yoktur.
Camgöbeği

21
Big-endian x86 işlemci diye bir şey yoktur. Ubuntu'yu biendian işlemcide (ARM veya MIPS gibi) çalıştırsanız bile, ELF yürütülebilir dosyaları her zaman büyük (MSB) veya küçük (LSB) endianlardır. Biendian çalıştırılabilir dosyaları oluşturulamaz, bu nedenle çalışma zamanı kontrolleri gerekmez.
Fabel

4
Bu yöntemdeki optimizasyonu kapatmak için 'geçici birleşme ...' kullanın Derleyiciye 'u' nun başka bir yerde değiştirilebileceğini ve verilerin yüklenmesi gerektiğini söyler
mishmashru

1
Bu fonksiyonun çalışma zamanında optimize ediciden farklı bir değer döndürmesi için, optimize edicinin hata yaptığını ima edeceğini hesaplar. Derleme sırasında (program boyunca) derleme sırasında bunlardan en az biriyle uyumsuz gibi görünen açık varsayımlara rağmen, farklı endianiteye sahip iki farklı mimaride taşınabilir olarak çalışabilecek derlenmiş optimize edilmiş ikili kod örnekleri olduğunu mu söylüyorsunuz? mimariler?
Scott

13

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.


8

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.


6

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.


1
FYI, "dört çeşit endianness" bağlantısı koptu,
Remy Lebeau

sabit ve yapılmış wiki
fuzzyTew

5

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.


3
Bu, kısa sürede tam olarak 2 bayt olan güvenceye dayanmaz.
sharptooth

3
Yine de soruda verilen iki mimariye dayanarak oldukça güvenli bir bahis olurdu.
Daemin

8
Başka bir platformda kısa olmanın farklı olmasına karşı gelecekteki kanıtları ekleyin stdint.hve kullanın int16_t.
Denise Skidmore

4

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.


satır içi yapabilir misin? satır içi statik değişkenlerin birden çok bellek bloğuna neden olup olmadığından emin değilim
aah134

4

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.


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

1
Bu eşdeğer olur mu? #define IS_BIGENDIAN() (*((char*) &((int){ 0x00ff })) == (0x00))
Emanuel

4

Kullanmayın union!

C ++, unions 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::endiangelen <type_traits>başlığındaki.

(Yazma sırasında, C ++ 20 henüz yayınlanmamıştır, ancak bir şeyin std::endiandahil 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.)

C ++ 20 Sonrası

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_castuygulama 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 charveya unsigned chartype.

C ++ 11 Sonrası

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 sonrası (numarasız)

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");

Bu başka bir çözüm. Andrew Hare'nin çözümüne benzer.


3

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
}

3

Bildirmek:

İlk yazım yanlış "derleme zamanı" olarak bildirildi. Mevcut C ++ standardında bile imkansız. Constexpr, fonksiyonun her zaman derleme zamanı hesaplaması yaptığı anlamına GELMEZ. Düzeltme için Richard Hodges'a teşekkürler.

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;
}

2
Uint8_t üzerinde imzasız karakter kullanmanızın özel bir nedeni var mı?
Kevin

0 çalışma zamanı yükü ... hoşuma gitti!
hanshenrik

Sanırım, bu yapı makinesinin endiannlarını tespit ediyor, hedefi değil?
hutorny

2
C ++ 'da bu UB değil mi?
rr-

6
bu bağlamda yasal değildir. Doğrudan başlatılmamış bir sendikanın üyesine erişemezsiniz. Önişlemci büyüsü olmadan derleme zamanında endianlığı yasal olarak tespit etmenin bir yolu yoktur.
Richard Hodges

2

Bunu ayrıca ön işlemciden boost endian gibi bulunabilecek boost header dosyası gibi bir şey kullanarak da yapabilirsiniz.


1

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!");
...

Bu değil misin __BYTE_ORDER__, __ORDER_LITTLE_ENDIAN__ve __ORDER_BIG_ENDIAN__?
Xeverous

1

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

Ne güzel bir fikir! Ve şimdi tamsayılarınızı ağ soketi üzerinden bilinmeyen cihaza aktaralım.
Maksym Ganenko

@MaksymGanenko Yorumunu almıyorum. İronik mi? Ben am değil tefrika verilerin endianness'ın belirtmez için düşündüren. Verileri alan makinenin endianitesine bağlı olarak kod yazmamanızı öneririm.
fjardon

@MaksymGanenko Eğer inerseniz, cevabın neden yanlış olduğunu açıklayabilirsiniz. En azından potansiyel okuyucuların neden cevabımı takip etmemeleri gerektiğini anlamalarına yardımcı olmak için.
fjardon


0

Buna ne dersin?

#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

İş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.


0

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.


-1

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.


Neden 2 baytlık bir değer üzerinde çalışmak için 4 bayt ayırmalıyım? Neden belirsiz bir değeri maskelemek 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ş.
Remy Lebeau

@RemyLebeau, cevabımın tamamı derleyici tarafından optimize edilmemiş bir kod üretmekti. Elbette, kodunuz çok daha basit, ancak optimizasyonlar açıkken derlendikten sonra sabit bir boole olacak. Cevabımda belirttiğim gibi, aslında C kodunu aynı yürütülebilir dosyanın her iki bayt emriyle çalışacak şekilde derlemenin bir yolu olup olmadığını bilmiyorum ve ayrıca çalışma zamanında kontrol yapıp yapamayacağımı merak ettim optimizasyonlara rağmen.
Tex Killer

@TexKiller neden kod optimizasyonlarını devre dışı bırakmıyorsunuz? Kullanmak volatile, veya #pragmavb.
Remy Lebeau

@RemyLebeau, o anahtar kelimeleri o zaman bilmiyordum ve sadece bildiklerimle derleyici optimizasyonunu önlemek için biraz zorlandım.
Tex Killer

-1

bunu belirlemenin hızlı ve standart bir yolu olmasa da, bu çıktıyı verecektir:

#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

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;
} 

-2

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.


int32t kullanılarak bu geliştirilebilir.
shuttle87

1
^ Nitpick yapmak istiyorsanız, burada en iyisi int16_fast_t. ve @ Archimedes520'nin geçerli kodu int'nin yerel olarak int8 olduğu bir kemer üzerinde çalışmaz;) (ilk etapta c standartlarına aykırı olabilir)
hanshenrik
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.