76

Is there a safe, portable way to determine (during compile time) the endianness of the platform that my program is being compiled on? I'm writing in C.

[EDIT] Thanks for the answers, I decided to stick with the runtime solution!

Ryan Haining
  • 35,360
  • 15
  • 114
  • 174
  • might be your solution http://stackoverflow.com/questions/2100331/c-macro-definition-to-determine-big-endian-or-little-endian-machine to detect it runtime – khachik Nov 21 '10 at 20:00
  • 1
    See my answer which should do it at compile-time, as long as you don't mind requiring (at least partial) C99 support in the compiler. – R.. GitHub STOP HELPING ICE Nov 21 '10 at 20:46
  • Why do you need to know? If you need to write in a format that is readable by another system use htonl() and family to convert to network byte order and back. On one system this will be the null operation. – Martin York Nov 21 '10 at 21:29
  • 2
    What's wrong with just using `#ifdef __LITTLE_ENDIAN__` etc ? – Paul R Nov 21 '10 at 21:43
  • 7
    @Paul: Who says `__LITTLE_ENDIAN__` is an indicator that the machine is little endian and not one of two macros (along with `__BIG_ENDIAN__`) which are possible values for `__BYTE_ORDER__`? You can't know. As soon as you start inspecting macro names that were reserved for the implementation, your're on the road to the dark world of UB. Good code never directly inspects macros beginning with `_[A-Z_]` but instead uses a `configure` script or similar to work out its environment then uses `#include "config.h"` and `#ifdef HAVE_FOO` etc. – R.. GitHub STOP HELPING ICE Nov 22 '10 at 06:20
  • Yes, the technique is demonstrated in t[his question](http://stackoverflow.com/questions/2100331/c-macro-definition-to-determine-big-endian-or-little-endian-machine). – EvilTeach Nov 21 '10 at 21:20
  • @LokiAstari: Why is it that whenever somebody asks a perfectly legit question, somebody comes along shouting "Don't do that" or "Why do you need to know that"? The world is much bigger than you can imagine, and there are no ARPA headers on a myriad of systems, because they have no such notion as a "network ". Especially on microconroller environments you do need to know endianess for portability abstraction, perferably at preprocessor time (which is even before compile time). – T-Bull Feb 19 '15 at 14:30
  • @T-Bull: Exactly becuase the world is more complex then you can imagine. Because sometime they are seeking a solution to a problem that does not need to be answered if you look at it from a different point of view. Asking to understand why they need to know something may help us understand the actual problem. We may find out that the question they ask is perfectly good for the situation they are trying to solve or alternatively we may find there are better ways of trying to solve the underlying problem. It is best never to assume anything. – Martin York Feb 19 '15 at 15:50
  • 2
    @LokiAstari: Well yeah, I understand your point of view, and even agree to the extent that there are many such questions where the questioner obviously lacks orientation so much that the question makes no sense at all. However, there're are also many replies (not answers) to perfectly valid questions which boil down to a stupid "Don't do that!" without any reasoning, and it appears I have a history of attracting such replies. At the least, I have the strong impression that SO is full of such people. Maybe addressing my comment to you was wrong, but with regard to SO, this /is/ a problem. – T-Bull May 21 '15 at 14:29

12 Answers12

47

To answer the original question of a compile-time check, there's no standardized way to do it that will work across all existing and all future compilers, because none of the existing C, C++, and POSIX standards define macros for detecting endianness.

But, if you're willing to limit yourself to some known set of compilers, you can look up each of those compilers' documentations to find out which predefined macros (if any) they use to define endianness. This page lists several macros you can look for, so here's some code which would work for those:

#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \
    defined(__BIG_ENDIAN__) || \
    defined(__ARMEB__) || \
    defined(__THUMBEB__) || \
    defined(__AARCH64EB__) || \
    defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__)
// It's a big-endian target architecture
#elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || \
    defined(__LITTLE_ENDIAN__) || \
    defined(__ARMEL__) || \
    defined(__THUMBEL__) || \
    defined(__AARCH64EL__) || \
    defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__)
// It's a little-endian target architecture
#else
#error "I don't know what architecture this is!"
#endif

If you can't find what predefined macros your compiler uses from its documentation, you can also try coercing it to spit out its full list of predefined macros and guess from there what will work (look for anything with ENDIAN, ORDER, or the processor architecture name in it). This page lists a number of methods for doing that in different compilers:

Compiler                   C macros                         C++ macros
Clang/LLVM                 clang -dM -E -x c /dev/null      clang++ -dM -E -x c++ /dev/null
GNU GCC/G++                gcc   -dM -E -x c /dev/null      g++     -dM -E -x c++ /dev/null
Hewlett-Packard C/aC++     cc    -dM -E -x c /dev/null      aCC     -dM -E -x c++ /dev/null
IBM XL C/C++               xlc   -qshowmacros -E /dev/null  xlc++   -qshowmacros -E /dev/null
Intel ICC/ICPC             icc   -dM -E -x c /dev/null      icpc    -dM -E -x c++ /dev/null
Microsoft Visual Studio (none)                              (none)
Oracle Solaris Studio      cc    -xdumpmacros -E /dev/null  CC      -xdumpmacros -E /dev/null
Portland Group PGCC/PGCPP  pgcc  -dM -E                     (none)

Finally, to round it out, the Microsoft Visual C/C++ compilers are the odd ones out and don't have any of the above. Fortunately, they have documented their predefined macros here, and you can use the target processor architecture to infer the endianness. While all of the currently supported processors in Windows are little-endian (_M_IX86, _M_X64, _M_IA64, and _M_ARM are little-endian), some historically supported processors like the PowerPC (_M_PPC) were big-endian. But more relevantly, the Xbox 360 is a big-endian PowerPC machine, so if you're writing a cross-platform library header, it can't hurt to check for _M_PPC.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • 2
    I'm not familiar with Microsoft's compilers, but ARM could potentially run in either endian mode. I'm not sure if it is possible to check at compile time for it. – Tyzoid Mar 10 '16 at 21:53
  • So, do you feel the snippet you provided is general enough? For all the compilers you listed at least? – einpoklum Apr 15 '16 at 09:18
  • @Tyzoid It should be, since the compiler will definitely know which endianness it is compiling for. – Robert Feb 10 '17 at 13:58
  • +1 This is a "messy" solution but one that works on common platforms and at least compiles everywhere else. – Petr Vepřek Dec 31 '18 at 07:42
  • C'mon, instead of one liner solution you suggest to list zillions of existing predefined macroses which may outdate at any moment. – Maksym Ganenko Oct 29 '19 at 09:11
  • Byte order macro in arm-none-eabi and x86_64-w64-mingw32 is defined as `#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__` – Reza May 15 '20 at 08:52
32

This is for compile time checking

You could use information from the boost header file endian.hpp, which covers many platforms.

edit for runtime checking

bool isLittleEndian()
{
    short int number = 0x1;
    char *numPtr = (char*)&number;
    return (numPtr[0] == 1);
}

Create an integer, and read its first byte (least significant byte). If that byte is 1, then the system is little endian, otherwise it's big endian.

edit Thinking about it

Yes you could run into a potential issue in some platforms (can't think of any) where sizeof(char) == sizeof(short int). You could use fixed width multi-byte integral types available in <stdint.h>, or if your platform doesn't have it, again you could adapt a boost header for your use: stdint.hpp

CharlesB
  • 86,532
  • 28
  • 194
  • 218
wkl
  • 77,184
  • 16
  • 165
  • 176
  • 69
    This does not answer the question; it's a runtime check, not a compiletime check. – R.. GitHub STOP HELPING ICE Nov 21 '10 at 20:42
  • 1
    @R. The sentence at the top is about endian.hpp which would allow you to do compile time checks via macro checks. – wkl Nov 21 '10 at 22:17
  • 3
    *Nod*. By the way, if `sizeof(char)==sizeof(short)`, then `uint8_t` cannot exist on the implementation. C99 requires `uint8_t` to have no padding and be exactly 8 bits, and also defines the representation of types in terms of `char`/bytes, so `uint8_t` can only exist if `CHAR_BIT==8`. But then `short` could not hold the required minimum range. :-) – R.. GitHub STOP HELPING ICE Nov 22 '10 at 06:17
  • reading the documentation of `endian.hpp`: it's not compile time checking the endianess. it's extracting the endianess from headers, if they're exposed. so it's not guaranteed to work. – Alexander Oh Jun 23 '14 at 00:46
  • Relying on header files of a specific library is not exactly portable, in this case even less so if you write in C, not C++. This is clearly not an answer to the question. Correct answers are further down. They have only one or two upvotes or even less. – T-Bull Feb 19 '15 at 14:19
  • 1
    what about using constexpr and shift operators for this purpose? – Sergei Krivonos Oct 23 '15 at 19:40
  • This is implementation-defined in C and undefined in C++ right? – Micha Wiedenmann Nov 28 '18 at 14:03
  • I love how so many compile-time solutions relying on the boost header trick broke with the 1.73 release because it was obviously an implementation `detail` header. – Jonas Müller May 05 '20 at 21:46
  • Is `sizeof(char) == sizeof(short int)` even possible? – user16217248 Sep 23 '22 at 20:29
  • 1
    @user16217248 according to the C standard, it is a situation that’s allowed in implementations, but not one I’ve seen in any actual implantation. The only guarantee is that `char` is the smallest unit in the implementation, and that a `short int` is at least as large as `char` and could hold the range `[-32768, 32767]`. – wkl Sep 23 '22 at 23:21
  • @wkl This would also require that `CHAR_BIT > 8` – user16217248 Sep 24 '22 at 02:19
  • There really exist DSP processors where `CHAR_BIT == 16`. Some of them are pretty widely used. – Ben Voigt May 16 '23 at 16:30
18

With C99, you can perform the check as:

#define I_AM_LITTLE (((union { unsigned x; unsigned char c; }){1}).c)

Conditionals like if (I_AM_LITTLE) will be evaluated at compile-time and allow the compiler to optimize out whole blocks.

I don't have the reference right off for whether this is strictly speaking a constant expression in C99 (which would allow it to be used in initializers for static-storage-duration data), but if not, it's the next best thing.

Matt Joiner
  • 112,946
  • 110
  • 377
  • 526
R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • 10
    No, it isn't, not even when you give it a `const` for the type. – Jens Gustedt Nov 21 '10 at 21:55
  • 2
    @einpoklum: A union's size is the the largest type, in this case, unsigned int (usually 4 bytes), so the initialization is essentially 'unsigned int x = 1;'. Using the 'c' field pulls off the first sizeof(unsigned char) bytes, or, essentially 'x & 0xff000000'. (At least, I assume that's how it works, I couldn't find documentation saying that.) So if big-endian, 'x' will be 00 00 00 01 and (x & 0xff000000) == 0 == false. If little-endian, 'x' will be 01 00 00 00, so (x & 0xff000000) == 1 == true. – prewett Jun 27 '16 at 23:55
  • 1
    @prewett: I actually meant "can you make the explanation part of the answer?" ... I actually do understand why this works myself, but the syntax might be a bit cryptic for some. – einpoklum Jun 28 '16 at 00:40
  • using boost in C++: #include static_assert( boost::endian::order::native == boost::endian::order::little, "you got some computer there!" ); – Scott Sep 12 '16 at 17:31
  • This does not work in Visual Studio, says it's not a constant expression – jjxtra Mar 29 '17 at 23:17
  • @jjxtra report a bug. If you try simplifying the expression by removing the define and breaking to multiple lines *(it would be easier though if you compile along the way by something working, for example gcc)*, you'll see that all it does in a nutshell is creating an object of the union, then accessing its member. That's definitely okay. – Hi-Angel Dec 27 '17 at 07:25
  • That's not a constant expression because it violates the rule that "Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to the sizeof operator." The cast here casts to an union type. See 6.6.1 of the C99 standard, http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf "An implementation may accept other forms of constant expressions." but I'm not aware of an implementation that accepts the above. – real-or-random Aug 13 '20 at 17:59
  • 1
    @real-or-random: There is no cast operator whatsoever in this expression. The only operator present is the `.` (member access) operator. However, it violates the rules on operands; compound literals are not one of the operand types allowed in arithmetic constant expressions. – R.. GitHub STOP HELPING ICE Aug 13 '20 at 18:05
  • @R..GitHubSTOPHELPINGICE Oh indeed, you're right, I wasn't really aware of compound literals. – real-or-random Aug 15 '20 at 10:15
11

Interesting read from the C FAQ:

You probably can't. The usual techniques for detecting endianness involve pointers or arrays of char, or maybe unions, but preprocessor arithmetic uses only long integers, and there is no concept of addressing. Another tempting possibility is something like

  #if 'ABCD' == 0x41424344

but this isn't reliable, either.

CharlesB
  • 86,532
  • 28
  • 194
  • 218
Daniel Băluţă
  • 1,255
  • 11
  • 13
  • 5
    Why is this not reliable? A multi-character constant is indeed the **only** valid way of doing it (in C++, C99 allows the union thing). The result is of course _implementation defined_, but that's what it has to be since the order of bytes (which is just what you're trying to figure out!) is implementation-defined. Every other method is either _undefined behavior_ (unions, type-punned pointers, etc), or total shit (using Boost, which is nothing but platform detection via #ifdef followed by a hand-coded #define). – Damon Feb 10 '16 at 15:01
  • One might be tempted to try with wide character literals in C++14 instead (I actually did try, since GCC annoyingly warns about multi-character literals) but it turns out that the standard is pretty prohibitive in what you can cast them to, and anything I tried fails with an error. Something like `if( (char[4])(U'A')[0] == 65)` would do, if the cast was legal. But the multi-character constant is indeed completely legal as-is. – Damon Feb 10 '16 at 15:05
  • @Damon: In case of cross-compilation, endianness of the target machine may be different than endianness of the compilation machine. Preprocessor would pick the latter one, I suppose. – Alexey Kruglov Jan 20 '21 at 14:43
8

I would like to extend the answers for providing a constexpr function for C++

union Mix {
    int sdat;
    char cdat[4];
};
static constexpr Mix mix { 0x1 };
constexpr bool isLittleEndian() {
    return mix.cdat[0] == 1;
}

Since mix is constexpr too it is compile time and can be used in constexpr bool isLittleEndian(). Should be safe to use.

Update

As @Cheersandhth pointed out below, these seems to be problematic.

The reason is, that it is not C++11-Standard conform, where type punning is forbidden. There can always only one union member be active at a time. With a standard conforming compiler you will get an error.

So, don't use it in C++. It seems, you can do it in C though. I leave my answer in for educational purposes :-) and because the question is about C...

Update 2

This assumes that int has the size of 4 chars, which is not always given as @PetrVepřek correctly pointed out below. To make your code truly portable you have to be more clever here. This should suffice for many cases though. Note that sizeof(char) is always 1, by definition. The code above assumes sizeof(int)==4.

towi
  • 21,587
  • 28
  • 106
  • 187
  • 3
    Aren't all these type-punning techniques implementation defined behaviour (or even undefined behaviour)? I currently don't know of implementations where reading a union meber different from the one written last really fails, but I guess strictly speaking it is non portable. – Drunix Apr 13 '15 at 16:20
  • @Drunix That is because using code that depends on the endianness of the machine is implementation defined (or undefined -- I can't remember which) behaviour. Therefore any code that detects endianness also uses implementation defined behaviour. I don't think that integers are required to be big or little endian, that is I think that a compiler is allowed to use something like PDP endian as long as the arithmetic operations are correct. If you want your code to be completely portable you would have to use an endian-independent method (e.g. `(data[0] << 8) | data[1]`). – qbt937 May 22 '15 at 05:42
  • @qbt937 Yes, endian sensitive code is implementation dependent, but the implications what should happen on each architecture is more or less clear. For reading a different member of a uninion other than the one that was written last it is not that clear. As I wrote before, this is currently more or less a standards discussion, in practice it should work anyway. – Drunix May 22 '15 at 06:38
  • @Drunix Yes, in practice this should work. I just wanted to make it clear that there isn't a way to check endianness without using implementation defined behaviour, as your comment might be interpreted as implying that there is a way (that doesn't use type punning) to check what the machine's endianness is without invoking implementation defined behaviour. – qbt937 May 22 '15 at 07:15
  • 2
    **–1** Fails to compile with clang (it's clever and a few weeks ago I thought it would work, then tested with clang and learned). – Cheers and hth. - Alf Feb 23 '16 at 01:12
  • 1
    @Cheersandhth.-Alf Indeed. Since Clang 3.2 you get the error `"read of member 'cdat' of union with active member 'sdat' is not allowed in a constant expression"`. I wasn't aware of "active members" of unions. I wonder if it is in the Standard? See here, http://goo.gl/Gs6qrG. Oh yes, http://stackoverflow.com/a/11996970/472245 explains, that C++11 disallows what C11 allows. Thanks. I'll update my answer. – towi Feb 23 '16 at 10:00
  • IIRC, type punning through char arrays is explicitly allowed. – Alexandre C. Feb 24 '16 at 10:25
  • @AlexandreC. Also in C++ (...11), or do you mean C? Even if, using a char-array *as-part-of a union* is probably not included in "type punning through char[] is allowed", it could still be forbidden via a union -- in contrast to accessing it through casted a pointer of the wring type. – towi Feb 25 '16 at 13:33
  • 1
    You can type pun by copying to a char array and back, but not through a union. – Alexandre C. Feb 25 '16 at 13:54
  • 1
    @Cheers, towi, can you check on a little modified solution in form of question: [Finding endian-ness programatically at compile-time using C++11](http://stackoverflow.com/q/39763611/514235). If it's not problematic, then probably I can post it as a solution in one of the endian-ness related questions. – iammilind Sep 29 '16 at 07:13
  • This assumes that size of int is four times the size of char. This is wrong. Int and char can, for example, have the same size (such as 32 bits). – Petr Vepřek Dec 31 '18 at 07:20
7

Use CMake TestBigEndian as

INCLUDE(TestBigEndian)
TEST_BIG_ENDIAN(ENDIAN)
IF (ENDIAN)
    # big endian
ELSE (ENDIAN)
    # little endian
ENDIF (ENDIAN)
Like
  • 1,470
  • 15
  • 21
  • Worth noting, that module has been superseded as of CMake 3.20 by the [`CMAKE__BYTE_ORDER`](https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_BYTE_ORDER.html) variable. – Alex Reinking Sep 28 '22 at 16:19
6

Not during compile time, but perhaps during runtime. Here's a C function I wrote to determine endianness:

/*  Returns 1 if LITTLE-ENDIAN or 0 if BIG-ENDIAN  */
#include <inttypes.h>
int endianness()
{
  union { uint8_t c[4]; uint32_t i; } data;
  data.i = 0x12345678;
  return (data.c[0] == 0x78);
}
pr1268
  • 1,176
  • 3
  • 9
  • 16
  • Birryree's answer and mine overlapped, but each of our examples appears to do pretty much the same thing. – pr1268 Nov 21 '10 at 20:04
  • 3
    Invokes UB, you can only read from the last union member that was written to. – GManNickG Nov 21 '10 at 21:50
  • 1
    @GMan: I agree it's ambiguous, but that seems to conflict with other parts of the standard that explicitly allow you to access an object's representation as an overlaid `char` array. – R.. GitHub STOP HELPING ICE Nov 22 '10 at 06:13
  • 1
    @R: Good point. Had it been a `char` that would have been fine, but `uint8_t` isn't (necessarily) a `char`. (Does that mean the behavior in this case is strictly implementation-defined, instead of undefined?) – GManNickG Nov 22 '10 at 06:21
5

From Finally, one-line endianness detection in the C preprocessor:

#include <stdint.h>

#define IS_BIG_ENDIAN (*(uint16_t *)"\0\xff" < 0x100)

Any decent optimizer will resolve this at compile-time. gcc does at -O1.

Of course stdint.h is C99. For ANSI/C89 portability see Doug Gwyn's Instant C9x library.

3

I took it from rapidjson library:

#define BYTEORDER_LITTLE_ENDIAN 0 // Little endian machine.
#define BYTEORDER_BIG_ENDIAN 1 // Big endian machine.

//#define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN

#ifndef BYTEORDER_ENDIAN
    // Detect with GCC 4.6's macro.
#   if defined(__BYTE_ORDER__)
#       if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
#           define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#       elif (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#           define BYTEORDER_ENDIAN BYTEORDER_BIG_ENDIAN
#       else
#           error "Unknown machine byteorder endianness detected. User needs to define BYTEORDER_ENDIAN."
#       endif
    // Detect with GLIBC's endian.h.
#   elif defined(__GLIBC__)
#       include <endian.h>
#       if (__BYTE_ORDER == __LITTLE_ENDIAN)
#           define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#       elif (__BYTE_ORDER == __BIG_ENDIAN)
#           define BYTEORDER_ENDIAN BYTEORDER_BIG_ENDIAN
#       else
#           error "Unknown machine byteorder endianness detected. User needs to define BYTEORDER_ENDIAN."
#       endif
    // Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro.
#   elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
#       define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#   elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)
#       define BYTEORDER_ENDIAN BYTEORDER_BIG_ENDIAN
    // Detect with architecture macros.
#   elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__)
#       define BYTEORDER_ENDIAN BYTEORDER_BIG_ENDIAN
#   elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__)
#       define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#   elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
#       define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#   else
#       error "Unknown machine byteorder endianness detected. User needs to define BYTEORDER_ENDIAN."
#   endif
#endif
nullptr
  • 3,320
  • 7
  • 35
  • 68
  • This was perfect! [Here](https://github.com/Tencent/rapidjson/blob/a95e013b97ca6523f32da23f5095fcc9dd6067e5/include/rapidjson/rapidjson.h#L245-L282) is where the code came from. It is MIT licensed – J. Rehbein Jul 30 '23 at 00:36
1

I once used a construct like this one:

uint16_t  HI_BYTE  = 0,
          LO_BYTE  = 1;
uint16_t  s = 1;

if(*(uint8_t *) &s == 1) {   
   HI_BYTE = 1;
   LO_BYTE = 0;
} 

pByte[HI_BYTE] = 0x10;
pByte[LO_BYTE] = 0x20;

gcc with -O2 was able to make it completely compile time. That means, the HI_BYTE and LO_BYTE variables were replaced entirely and even the pByte acces was replaced in the assembler by the equivalent of *(unit16_t *pByte) = 0x1020;.

It's as compile time as it gets.

Patrick Schlüter
  • 11,394
  • 1
  • 43
  • 48
0

To my knowledge no, not during compile time.

At run-time, you can do trivial checks such as setting a multi-byte value to a known bit string and inspect what bytes that results in. For instance using a union,

typedef union {
    uint32_t word;
    uint8_t bytes[4];
} byte_check;

or casting,

uint32_t word;
uint8_t * bytes = &word;

Please note that for completely portable endianness checks, you need to take into account both big-endian, little-endian and mixed-endian systems.

Christoffer
  • 12,712
  • 7
  • 37
  • 53
  • hmm, it's not too difficult to do in runtime I guess... using some pointer casting, like so: char p[] = {0, 1}; short* ptr = (short*)p; if(*ptr == 1){ we're big endian}, am I right? –  Nov 21 '10 at 20:01
-2

For my part, I decided to use an intermediate approach: try the macros, and if they don't exist, or if we can't find them, then do it in runtime. Here is one that works on the GNU-compiler:

#define II      0x4949     // arbitrary values != 1; examples are
#define MM      0x4D4D     // taken from the TIFF standard

int
#if defined __BYTE_ORDER__ && __BYTE_ORDER__ == __LITTLE_ENDIAN
     const host_endian = II;
# elif defined __BYTE_ORDER__ && __BYTE_ORDER__ == __BIG__ENDIAN
     const host_endian = MM;
#else
#define _no_BYTE_ORDER
     host_endian = 1;            // plain "int", not "int const" !
#endif

and then, in the actual code:

int main(int argc, char **argv) {
#ifdef _no_BYTE_ORDER
    host_endian = * (char *) &host_endian ? II : MM;
#undef _no_BYTE_ORDER
#endif

// .... your code here, for instance:
printf("Endedness: %s\n", host_endian == II ? "little-endian"
                                            : "big-endian");

return 0;
}

On the other hand, as the original poster noted, the overhead of a runtime check is so little (two lines of code, and micro-seconds of time) that it's hardly worth the bother to try and do it in the preprocessor.

MSohnius
  • 23
  • 3