C Macro definition to determine big endian or litt

2019-01-01 14:55发布

问题:

Is there a one line macro definition to determine the endianness of the machine. I am using the following code but converting it to macro would be too long.

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

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

回答1:

Code supporting arbitrary byte orders, ready to be put into a file called 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

You would check for little endian systems via

O32_HOST_ORDER == O32_LITTLE_ENDIAN


回答2:

If you have a compiler that supports C99 compound literals:

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

or:

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

In general though, you should try to write code that does not depend on the endianness of the host platform.


Example of host-endianness-independent implementation of 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:

There is no standard, but on many systems including <endian.h> will give you some defines to look for.



回答4:

To detect endianness at run time, you have to be able to refer to memory. If you stick to standard C, declarating a variable in memory requires a statement, but returning a value requires an expression. I don\'t know how to do this in a single macro—this is why gcc has extensions :-)

If you\'re willing to have a .h file, you can define

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

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

and then you can use the ENDIANNESS macro as you will.



回答5:

If you want to only rely on the preprocessor, you have to figure out the list of predefined symbols. Preprocessor arithmetics has no concept of addressing.

GCC on Mac defines __LITTLE_ENDIAN__ or __BIG_ENDIAN__

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

Then, you can add more preprocessor conditional directives based on platform detection like #ifdef _WIN32 etc.



回答6:

I believe this is what was asked for. I only tested this on a little endian machine under msvc. Someone plese confirm on a big endian machine.

    #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

As a side note (compiler specific), with an aggressive compiler you can use \"dead code elimination\" optimization to achieve the same effect as a compile time #if like so:

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

The above relies on the fact that the compiler recognizes the constant values at compile time, entirely removes the code within if (false) { ... } and replaces code like if (true) { foo(); } with foo(); The worst case scenario: the compiler does not do the optimization, you still get correct code but a bit slower.



回答7:

If you are looking for a compile time test and you are using gcc, you can do:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__

See gcc documentation for more information.



回答8:

You can in fact access the memory of a temporary object by using a compound literal (C99):

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

Which GCC will evaluate at compile time.



回答9:

The \'C network library\' offers functions to handle endian\'ness. Namely htons(), htonl(), ntohs() and ntohl() ...where n is \"network\" (ie. big-endian) and h is \"host\" (ie. the endian\'ness of the machine running the code).

These apparent \'functions\' are (commonly) defined as macros [see <netinet/in.h>], so there is no runtime overhead for using them.

The following macros use these \'functions\' to evaluate endian\'ness.

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

In addition:

The only time I ever need to know the endian\'ness of a system is when I write-out a variable [to a file/other] which may be read-in by another system of unknown endian\'ness (for cross-platform compatability) ...In cases such as these, you may prefer to use the endian functions directly:

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


回答10:

Use an inline function rather than a macro. Besides, you need to store something in memory which is a not-so-nice side effect of a macro.

You could convert it to a short macro using a static or global variable, like this:

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


回答11:

Whilst there is no portable #define or something to rely upon, platforms do provide standard functions for converting to and from your \'host\' endian.

Generally, you do storage - to disk, or network - using \'network endian\', which is BIG endian, and local computation using host endian (which on x86 is LITTLE endian). You use htons() and ntohs() and friends to convert between the two.



回答12:

Try this:

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

   TEST;
}


回答13:

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


回答14:

Don\'t forget that endianness is not the whole story - the size of char might not be 8 bits (e.g. DSP\'s), two\'s complement negation is not guaranteed (e.g. Cray), strict alignment might be required (e.g. SPARC, also ARM springs into middle-endian when unaligned), etc, etc.

It might be a better idea to target a specific CPU architecture instead.

For example:

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

Note that this solution is also not ultra-portable unfortunately, as it depends on compiler-specific definitions (there is no standard, but here\'s a nice compilation of such definitions).



回答15:

My answer is not as asked but It is really simple to find if your system is little endian or big endian?

Code:

#include<stdio.h>

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

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


回答16:

C Code for checking whether a system is little-endian or big-indian.

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


回答17:

Quiet late but... If you absolutely must have a macro AND ultra-portable code, detect and set it from your built environment (cmake/autotools).

Here\'s a simple program to just get it done, suitable for grepping:

#if __STDC_VERSION__ < 199901L
#error \"Requires C99 compatibility\"
#endif
#include <stdint.h>
#include <stdio.h>

const char MAGIC[4] = {0xDE, 0xAD, 0xBE, 0xEF};

int
main(void) {
  uint32_t magical = *(const uint32_t *)MAGIC;
  switch(magical) {
    case 0xEFBEADDE: printf(\"little\\n\"); break;
    case 0xDEADBEEF: printf(\"big\\n\"); break;
    case 0xADDEEFBE: printf(\"pdp\\n\"); break;
    default: for(; magical; magical >>= 8) {
        switch(magical & 0xff) {
          case 0xDE: printf(\"3\"); break;
          case 0xAD: printf(\"2\"); break;
          case 0xBE: printf(\"1\"); break;
          default: printf(\"0\"); } 
      } printf(\"\\n\");}
  return (0);
}


回答18:

Macro to find endiannes

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

or

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