C Büyük endian veya küçük endian makineyi belirlemek için makro tanım?


107

Makinenin dayanıklılığını belirlemek için tek satırlık bir makro tanımı var mı? Aşağıdaki kodu kullanıyorum ancak makroya dönüştürmek çok uzun olacaktır.

unsigned char test_endian( void )
{
    int test_var = 1;
    unsigned char *test_endian = (unsigned char*)&test_var;

    return (test_endian[0] == 0);
}

2
Neden aynı kodu bir makroya dahil etmiyorsunuz?
Sharptooth

4
Yalnızca C ön işlemcisiyle taşınabilirliği belirleyemezsiniz. Ayrıca son testiniz 0yerine NULL, test_endiannesnelerden birini başka bir şeye değiştirmek istiyorsunuz :-).
Alok Singhal

2
Ayrıca bir makro neden gereklidir? Satır içi işlevi aynı şeyi yapar ve çok daha güvenlidir.
Sharptooth

13
@Sharptooth, bir makro caziptir çünkü değeri derleme sırasında bilinebilir, bu da örneğin şablon somutlaştırmayı kontrol etmek için platformunuzun dayanıklılığını kullanabileceğiniz veya hatta bir #ifyönergeyle farklı kod blokları seçebileceğiniz anlamına gelir .
Rob Kennedy

3
Bu doğru ama verimsiz. Little-endian işlemcim varsa ve küçük endian verilerini kabloya veya bir dosyaya yazıyorsam, verileri hiçbir amaç için açıp yeniden paketlemekten kaçınmayı tercih ederim. Geçim için video sürücüleri yazardım. Öyle derece sen her yere optimize etmek için bir ekran kartına pikselleri yazarken önemli.
Edward Falk

Yanıtlar:


102

Rasgele bayt siparişlerini destekleyen kod, şu adla adlandırılan bir dosyaya konulmaya hazır order32.h:

#ifndef ORDER32_H
#define ORDER32_H

#include <limits.h>
#include <stdint.h>

#if CHAR_BIT != 8
#error "unsupported char size"
#endif

enum
{
    O32_LITTLE_ENDIAN = 0x03020100ul,
    O32_BIG_ENDIAN = 0x00010203ul,
    O32_PDP_ENDIAN = 0x01000302ul,      /* DEC PDP-11 (aka ENDIAN_LITTLE_WORD) */
    O32_HONEYWELL_ENDIAN = 0x02030001ul /* Honeywell 316 (aka ENDIAN_BIG_WORD) */
};

static const union { unsigned char bytes[4]; uint32_t value; } o32_host_order =
    { { 0, 1, 2, 3 } };

#define O32_HOST_ORDER (o32_host_order.value)

#endif

Küçük endian sistemlerini şu yolla kontrol edersiniz:

O32_HOST_ORDER == O32_LITTLE_ENDIAN

11
Bu, çalışma zamanına kadar bitişe karar vermenize izin vermez . Aşağıdakiler derlenemiyor çünkü. / ** isLittleEndian :: sonuç -> 0 veya 1 * / struct isLittleEndian {enum isLittleEndianResult {sonuç = (O32_HOST_ORDER == O32_LITTLE_ENDIAN)}; };
user48956

3
Çalışma zamanına kadar sonuç almak imkansız mı?
k06a

8
Neden char? uint8_tBu tür mevcut değilse (kontrol edilebilir #if UINT8_MAX) daha iyi kullanın ve başarısız olun . Bunun CHAR_BITbağımsız olduğunu unutmayın uint8_t.
Andreas Spindler

2
Bu, c ++ 'da
UB'dir

3
Tamlık için karışıma bir tane daha O32_HONEYWELL_ENDIAN = 0x02030001ul /* Honeywell 316 */
Edward Falk

49

C99 bileşik değişmez değerlerini destekleyen bir derleyiciniz varsa:

#define IS_BIG_ENDIAN (!*(unsigned char *)&(uint16_t){1})

veya:

#define IS_BIG_ENDIAN (!(union { uint16_t u16; unsigned char c; }){ .u16 = 1 }.c)

Genel olarak, ana bilgisayar platformunun sonluluğuna bağlı olmayan bir kod yazmaya çalışmalısınız.


Aşağıdakilerin ana bilgisayar-endianness-bağımsız uygulama örneği ntohl():

uint32_t ntohl(uint32_t n)
{
    unsigned char *np = (unsigned char *)&n;

    return ((uint32_t)np[0] << 24) |
        ((uint32_t)np[1] << 16) |
        ((uint32_t)np[2] << 8) |
        (uint32_t)np[3];
}

3
"Ana platformun sonluluğuna bağlı olmayan bir kod yazmaya çalışmalısınız". Ne yazık ki savunmam, "Bir POSIX uyumluluk katmanı yazdığımızı biliyorum, ancak ntoh'u uygulamak istemiyorum, çünkü bu, ana bilgisayar platformunun sonluluğuna bağlıdır" her zaman sağır kulaklara düştü ;-). Grafik formatı işleme ve dönüştürme kodu, gördüğüm diğer ana aday - her şeyi her zaman ntohl çağırmak istemezsiniz.
Steve Jessop

5
ntohlAna platformun dayanıklılığına bağlı olmayan bir şekilde uygulayabilirsiniz .
caf

1
@caf ev sahibi-endianness bağımsız bir şekilde ntohl'u nasıl yazarsınız?
Hayri Uğur Koltuk

3
@AliVeli: Cevaba örnek bir uygulama ekledim.
caf

6
Ayrıca kayıt için şunu da eklemeliyim ki, "(* (uint16_t *)" \ 0 \ xff "<0x100)", en azından gcc 4.5.2 ile ne kadar optimize edersem de bir sabitte derlenmeyecektir. Her zaman çalıştırılabilir kod oluşturur.
Edward Falk

43

Bir standart yoktur, ancak dahil olmak üzere birçok sistemde <endian.h>aramanız için bazı tanımlar verir.


30
Endianness'ı #if __BYTE_ORDER == __LITTLE_ENDIANve ile test edin #elif __BYTE_ORDER == __BIG_ENDIAN. Ve başka bir şekilde oluşturun #error.
To1ne

6
<endian.h>Windows'ta mevcut değil
rustyx

2
Android ve Krom projeleri kullanmak endian.hsürece __APPLE__veya _WIN32tanımlanır.
patryk.beza

1
OpenBSD 6.3'te <endian.h> , adların önünde alt çizgi olmadan #if BYTE_ORDER == LITTLE_ENDIAN(veya BIG_ENDIAN) sağlar . _BYTE_ORDERyalnızca sistem başlıkları içindir. __BYTE_ORDERmevcut değil.
George Koehler

@ To1ne Windows (en azından şu anda) yalnızca x86 ve ARM makinelerde çalıştığı için Endianness'in Windows için uygun olduğundan şüpheliyim. x86 her zaman LE ve ARM her iki mimariyi kullanacak şekilde yapılandırılabilir.
SimonC

27

Çalışma zamanında sonu algılamak için, belleğe başvurmanız gerekir. Standart C'ye sadık kalırsanız, bellekte bir değişken bildirmek bir ifade gerektirir, ancak bir değer döndürmek bir ifade gerektirir. Bunu tek bir makroda nasıl yapacağımı bilmiyorum — bu nedenle gcc'nin uzantıları vardır :-)

Bir .h dosyasına sahip olmak istiyorsanız,

static uint32_t endianness = 0xdeadbeef; 
enum endianness { BIG, LITTLE };

#define ENDIANNESS ( *(const char *)&endianness == 0xef ? LITTLE \
                   : *(const char *)&endianness == 0xde ? BIG \
                   : assert(0))

ve sonra ENDIANNESSmakroyu istediğiniz gibi kullanabilirsiniz .


6
Bunu seviyorum çünkü küçük ve büyük dışında endianness'ın varlığını kabul ediyor.
Alok Singhal

6
Bundan bahsetmişken, INT_ENDIANNESS, hatta UINT32_T_ENDIANNESS makrosunu çağırmaya değer olabilir, çünkü yalnızca bir türün depolama temsilini test eder. İntegral türlerinin küçük, ancak çiftlerin orta-endian olduğu bir ARM ABI vardır (her kelime küçüktür, ancak içinde işaret biti olan kelime diğer kelimeden önce gelir). Bu, derleyici ekibi arasında bir iki gün boyunca biraz heyecan yarattı, size söyleyebilirim.
Steve Jessop

19

Yalnızca ön işlemciye güvenmek istiyorsanız, önceden tanımlanmış sembollerin listesini bulmanız gerekir. Önişlemci aritmetiği, adresleme kavramına sahip değildir.

Mac'te GCC , __LITTLE_ENDIAN__veya__BIG_ENDIAN__

$ gcc -E -dM - < /dev/null |grep ENDIAN
#define __LITTLE_ENDIAN__ 1

Ardından, platform algılamaya dayalı daha fazla önişlemci koşullu yönergesi ekleyebilirsiniz #ifdef _WIN32.


6
Linux'ta GCC 4.1.2, bu makroları tanımlamıyor gibi görünse de, GCC 4.0.1 ve 4.2.1 bunları Macintosh'ta tanımlamaktadır. Bu nedenle, hangi derleyiciyi kullanacağınızı belirlemenize izin verildiğinde bile, platformlar arası geliştirme için güvenilir bir yöntem değildir.
Rob Kennedy

1
oh evet çünkü sadece Mac'te GCC tarafından tanımlanıyor.
Gregory Pakosz

Not: My GCC (Mac'te) #define __BIG_ENDIAN__ 1ve #define _BIG_ENDIAN 1.

OpenBSD / amd64 için clang 5.0.1 vardır #define __LITTLE_ENDIAN__ 1. Bu makro bir gcc özelliği değil, bir clang özelliği gibi görünüyor. gccBazı Mac'ler komut 's çınlama, gcc değil.
George Koehler

Mac'te GCC 4.2.1 o zamanlar GCC idi
Gregory Pakosz

15

Sanırım bunun istendiğine inanıyorum. Bunu yalnızca msvc altında küçük bir endian makinesinde test ettim. Birisi büyük bir endian makinesinde onayladı.

    #define LITTLE_ENDIAN 0x41424344UL 
    #define BIG_ENDIAN    0x44434241UL
    #define PDP_ENDIAN    0x42414443UL
    #define ENDIAN_ORDER  ('ABCD') 

    #if ENDIAN_ORDER==LITTLE_ENDIAN
        #error "machine is little endian"
    #elif ENDIAN_ORDER==BIG_ENDIAN
        #error "machine is big endian"
    #elif ENDIAN_ORDER==PDP_ENDIAN
        #error "jeez, machine is PDP!"
    #else
        #error "What kind of hardware is this?!"
    #endif

Bir yan not olarak (derleyiciye özel), agresif bir derleyici ile "ölü kod eleme" optimizasyonunu aşağıdaki gibi bir derleme zamanıyla aynı etkiyi elde etmek için kullanabilirsiniz #if:

    unsigned yourOwnEndianSpecific_htonl(unsigned n)
    {
        static unsigned long signature= 0x01020304UL; 
        if (1 == (unsigned char&)signature) // big endian
            return n;
        if (2 == (unsigned char&)signature) // the PDP style
        {
            n = ((n << 8) & 0xFF00FF00UL) | ((n>>8) & 0x00FF00FFUL);
            return n;
        }
        if (4 == (unsigned char&)signature) // little endian
        {
            n = (n << 16) | (n >> 16);
            n = ((n << 8) & 0xFF00FF00UL) | ((n>>8) & 0x00FF00FFUL);
            return n;
        }
        // only weird machines get here
        return n; // ?
    }

Yukarıdaki tamamen içinde kod kaldırır, derleyici derleme zamanında sabit değerler tanır gerçeğine dayanır if (false) { ... }ve benzeri cümledeki kodu if (true) { foo(); }ile foo();kötü durum senaryosu: optimizasyon yapmaz derleyici, yine biraz daha yavaş doğru kodu ama olsun.


Bu yöntemi beğendim, ancak yanılıyorsam düzeltin: bu yalnızca inşa ettiğiniz makinede derleme yaparken işe yarar, değil mi?
leetNightshade

3
gcc ayrıca, çok karakterli karakter sabitleri nedeniyle bir hata atar. Bu nedenle taşınabilir değil.
Edward Falk

2
hangi derleyici yazmanıza izin veriyor 'ABCD'?
Ryan Haining

2
Birçok derleyici, rahat uyum modlarında çok baytlı karakter sabitlerine izin verir, ancak üst kısmı ile çalıştırır clang -Wpedantic -Werror -Wall -ansi foo.cve hata verir. (Clang ve bu özellikle: -Wfour-char-constants -Werror)

@Edward Falk Kodda çok karakterli sabit olması hata değildir . Uygulama tanımlı davranıştır C11 6.4.4.4. 10. gcc ve diğerleri ayarlara bağlı olarak uyarıda bulunabilir / hata yapmayabilir, ancak bu bir C hatası değildir. Çok karakterli karakter sabitlerini kullanmak kesinlikle popüler değildir.
chux - Monica'yı eski durumuna getir

10

Derleme zamanı testi arıyorsanız ve gcc kullanıyorsanız, şunları yapabilirsiniz:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__

Daha fazla bilgi için gcc belgelerine bakın .


3
Bu kesinlikle gcc kullanan herkes için en iyi cevap
rtpax

2
__BYTE_ORDER__GCC 4.6
Benoit Blanchon

8

Sen can aslında erişimde bir bileşik değişmezi (C99) kullanarak geçici nesnenin hafıza:

#define IS_LITTLE_ENDIAN (1 == *(unsigned char *)&(const int){1})

Hangi GCC derleme zamanında değerlendirecektir.


Bunu sevdim. C99 altında derlediğinizi bilmenin taşınabilir, derleme zamanı bir yolu var mı?
Edward Falk

1
Oh, peki ya GCC değilse?
Edward Falk

1
@EdwardFalk Evet. #if __STDC_VERSION__ >= 199901L.
Jens

7

'C ağ kitaplığı', sonsuzluğun üstesinden gelmek için işlevler sunar. Yani htons (), htonl (), ntohs () ve ntohl () ... burada n "ağ" (yani, big-endian) ve h "host" (yani, çalıştıran makinenin endianlığı) kodu).

Bu görünen 'işlevler' (genellikle) makrolar olarak tanımlanır [bkz. <Netinet / in.h>], bu nedenle bunları kullanmak için çalışma zamanı yükü yoktur.

Aşağıdaki makrolar, sonsuzluğu değerlendirmek için bu 'işlevleri' kullanır.

#include <arpa/inet.h>
#define  IS_BIG_ENDIAN     (1 == htons(1))
#define  IS_LITTLE_ENDIAN  (!IS_BIG_ENDIAN)

Ek olarak:

Bir sistemin sonunu bilmem gereken tek zaman, sonu bilinmeyen başka bir sistem tarafından okunabilecek bir değişkeni [bir dosyaya / diğerine] yazdığım zamandır (platformlar arası uyumluluk için) ) ... Bu gibi durumlarda, endian işlevlerini doğrudan kullanmayı tercih edebilirsiniz:

#include <arpa/inet.h>

#define JPEG_MAGIC  (('J'<<24) | ('F'<<16) | ('I'<<8) | 'F')

// Result will be in 'host' byte-order
unsigned long  jpeg_magic = JPEG_MAGIC;

// Result will be in 'network' byte-order (IE. Big-Endian/Human-Readable)
unsigned long  jpeg_magic = htonl(JPEG_MAGIC);

Bu, sonsuzluğu belirlemenin hızlı bir yolunu arayan soruyu gerçekten cevaplamıyor.
Oren

@Oren: Geçerli eleştirinizle ilgili olarak, orijinal soruyu daha doğrudan ele alan ayrıntıyı öne ekledim.
BlueChip

6

Bir makro yerine bir satır içi işlev kullanın. Ayrıca, bir makronun pek de hoş olmayan bir yan etkisi olan bir şeyi hafızaya kaydetmeniz gerekir.

Şunun gibi statik veya global bir değişken kullanarak onu kısa bir makroya dönüştürebilirsiniz:

static int s_endianess = 0;
#define ENDIANESS() ((s_endianess = 1), (*(unsigned char*) &s_endianess) == 0)

bence bu en basit olanı olduğu için en iyisi. ancak karma endian'a karşı test yapmıyor
Hayri Uğur Koltuk

1
s_endianessBaşlamak için neden 1 olarak ayarlanmadı?
SquareRootOfTwentyThree

5

Taşınabilir bir #define veya güvenilecek bir şey olmasa da, platformlar 'ana bilgisayar' endianınıza ve 'ana bilgisayar' endianınıza dönüştürme için standart işlevler sağlar.

Genellikle, depolama - diske veya ağa - BÜYÜK endian olan 'ağ endian'ı' ve ana bilgisayar endian'ı ( x86'da LITTLE endian olan) kullanarak yerel hesaplama yaparsınız . Sen kullanmak htons()ve ntohs()ve arkadaşlar ikisi arasında dönüştürmek için.


4
#include <stdint.h>
#define IS_LITTLE_ENDIAN (*(uint16_t*)"\0\1">>8)
#define IS_BIG_ENDIAN (*(uint16_t*)"\1\0">>8)

6
Bu aynı zamanda bir sabit değil çalıştırılabilir kod üretir. "# İf IS_BIG_ENDIAN" yapamazsınız
Edward Falk

Bu çözümü, anladığım kadarıyla C / C ++ standartlarının tanımlanmamış davranışına dayanmadığı için seviyorum. Derleme zamanı değil ama bunun için tek standart çözüm c ++ 20
std'yi

4

O endianness'ın unutma bütün hikaye değil - büyüklüğü charkudretini 8 bit ikiye tümleme olumsuzluk sıkı hizalama gerekebilir, (mesela Cray) garanti edilmemektedir (DSP en örn) (örn SPARC, ayrıca içine yaylar ARM olamaz ortada - hizalanmadığında endian ), vb.

Bunun yerine belirli bir CPU mimarisini hedeflemek daha iyi bir fikir olabilir .

Örneğin:

#if defined(__i386__) || defined(_M_IX86) || defined(_M_IX64)
  #define USE_LITTLE_ENDIAN_IMPL
#endif

void my_func()
{
#ifdef USE_LITTLE_ENDIAN_IMPL
  // Intel x86-optimized, LE implementation
#else
  // slow but safe implementation
#endif
}

Derleyiciye özgü tanımlara bağlı olduğu için bu çözümün maalesef ultra taşınabilir olmadığını da unutmayın (standart yoktur, ancak işte bu tür tanımların güzel bir derlemesi).


3

Bunu dene:

#include<stdio.h>        
int x=1;
#define TEST (*(char*)&(x)==1)?printf("little endian"):printf("Big endian")
int main()
{

   TEST;
}

2

Lütfen buradaki yanıtların çoğunun taşınabilir olmadığına dikkat edin, çünkü bugün derleyiciler bu yanıtları derleme zamanında değerlendirecek (optimizasyona bağlıdır) ve gerçek makine dayanıklılığı farklı olabilirken belirli bir süreye göre belirli bir değer döndürecektir. Sonuculuğun test edildiği değerler asla sistem belleğine ulaşmayacaktır, bu nedenle gerçek yürütülen kod, gerçek sonsuzluktan bağımsız olarak aynı sonucu döndürecektir.

İçin örneğin , ARM Cortex-M3 uygulanan endian bir durum biti AIRCR.ENDIANNESS içinde yansıtacaktır ve derleyici derleme zamanında bu değeri bilemez.

Burada önerilen yanıtlardan bazıları için derleme çıktısı:

Bu cevap için https://godbolt.org/z/GJGNE2 ,

https://godbolt.org/z/Yv-pyJ için bu yanıt vb.

Bunu çözmek için volatileniteleyiciyi kullanmanız gerekecek . Yogeesh H Tbireyin cevabı bugünün gerçek hayat kullanımı için en yakın biridir, ancak o zamandan beri Christophdaha kapsamlı bir çözüm önerir, onun için hafif bir düzeltme cevap cevap eksiksiz, sadece eklemek yapacak volatilesendika bildirisine: static const volatile union.

Bu, kalıcılığı belirlemek için gerekli olan hafızadan depolamayı ve okumayı garanti eder.


2

Ön işlemciyi dökerseniz #defines

gcc -dM -E - < /dev/null
g++ -dM -E -x c++ - < /dev/null

Genellikle sana yardımcı olacak şeyler bulabilirsin. Derleme zamanı mantığı ile.

#define __LITTLE_ENDIAN__ 1
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__

Bununla birlikte, çeşitli derleyicilerin farklı tanımları olabilir.


0

Cevabım sorulduğu gibi değil ama sisteminizin küçük endian mı yoksa büyük endian mı olduğunu bulmak gerçekten basit mi?

Kod:

#include<stdio.h>

int main()
{
  int a = 1;
  char *b;

  b = (char *)&a;
  if (*b)
    printf("Little Endian\n");
  else
    printf("Big Endian\n");
}

0

C Bir sistemin küçük yerli mi yoksa büyük Hintli mi olduğunu kontrol etmek için kod.

int i = 7;
char* pc = (char*)(&i);
if (pc[0] == '\x7') // aliasing through char is ok
    puts("This system is little-endian");
else
    puts("This system is big-endian");

-3

Endiannes bulmak için makro

#define ENDIANNES() ((1 && 1 == 0) ? printf("Big-Endian"):printf("Little-Endian"))

veya

#include <stdio.h>

#define ENDIAN() { \
volatile unsigned long ul = 1;\
volatile unsigned char *p;\
p = (volatile unsigned char *)&ul;\
if (*p == 1)\
puts("Little endian.");\
else if (*(p+(sizeof(unsigned long)-1)) == 1)\
puts("Big endian.");\
else puts("Unknown endian.");\
}

int main(void) 
{
       ENDIAN();
       return 0;
}

3
İlk makro yanlıştır ve her zaman "Big-Endian" döndürür. Bit değiştirme, bitkinlikten etkilenmez - kalıcılık yalnızca okumaları etkiler ve belleğe kaydeder.
GaspardP
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.