Macro definition to determine big endian or little endian machine?

Viewed 121274

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] == 0);
}
22 Answers

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

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

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

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.

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.

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)

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.

If you dump the preprocessor #defines

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

You can usually find stuff that will help you. With compile time logic.

#define __LITTLE_ENDIAN__ 1
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__

Various compilers may have different defines however.

Try this:

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

   TEST;
}

Please pay attention that most of the answers here are not portable, since compilers today will evaluate those answers in compilation time (depends on the optimization) and return a specific value based on a specific endianness, while the actual machine endianness can differ. The values on which the endianness is tested, won't never reach the system memory thus the real executed code will return the same result regardless of the actual endianness.

For example, in ARM Cortex-M3 the implemented endianness will reflect in a status bit AIRCR.ENDIANNESS and compiler cannot know this value in compile time.

Compilation output for some of the answers suggested here:

https://godbolt.org/z/GJGNE2 for this answer,

https://godbolt.org/z/Yv-pyJ for this answer, and so on.

To solve it you will need to use the volatile qualifier. Yogeesh H T's answer is the closest one for today's real life usage, but since Christoph suggests more comprehensive solution, a slight fix to his answer would make the answer complete, just add volatile to the union declaration: static const volatile union.

This would assure storing and reading from memory, which is needed to determine endianness.

This question is actual for cpp too, so I asked here.

ONLY #if __cplusplus > 201703L

#include <bit>
#include <iostream>

using namespace std;

int main()
{
    if constexpr (endian::native == endian::big)
        cout << "big-endian";
    else if constexpr (endian::native == endian::little)
        cout << "little-endian";
    else
        cout << "mixed-endian";
}

For more info: https://en.cppreference.com/w/cpp/types/endian

If your compiler supports compound literals and you are pointedly not using C++ you can use

#define BIG_ENDIAN      ((*(const char*)&(const int){0x01020304}) == 0x01)
#define LITTLE_ENDIAN   ((*(const char*)&(const int){0x01020304}) == 0x04)

This doesn't require the declaration of any runtime variables, which I think makes it a good deal cleaner than most of the other solutions

If boost is available then you can use Boost.Predef which contains various predefined macros for the target platform including endianness (BOOST_ENDIAN_*). Yes boost is often thought as a C++ library, but this one is a preprocessor header that works with C as well! It allows you to detect endian in compile time portably

This library defines a set of compiler, architecture, operating system, library, and other version numbers from the information it can gather of C, C++, Objective C, and Objective C++ predefined macros or those defined in generally available headers. The idea for this library grew out of a proposal to extend the Boost Config library to provide more, and consistent, information than the feature definitions it supports. What follows is an edited version of that brief proposal.

For example

#include <boost/predef.h>
// or just include the necessary header
// #include <boost/predef/other/endian.h>

#if   BOOST_ENDIAN_BIG_BYTE
#elif BOOST_ENDIAN_LITTLE_BYTE
#elif BOOST_ENDIAN_LITTLE_WORD
...
#endif

More details can be found in BOOST_ENDIAN_* section

Demo on Godbolt


Note that it obviously can't detect bi-endian platforms where the endian can be changed during runtime

The detection is conservative in that it only identifies endianness that it knows for certain. In particular bi-endianness is not indicated as is it not practically possible to determine the endianness from anything but an operating system provided header. And the currently known headers do not define that programatic bi-endianness is available.

Related