Detecting endianness programmatically in a C++ program
Asked Answered
P

29

244

Is there a programmatic way to detect whether or not you are on a big-endian or little-endian architecture? I need to be able to write code that will execute on an Intel or PPC system and use exactly the same code (i.e., no conditional compilation).

Previse answered 16/6, 2009 at 12:56 Comment(4)
For the sake of completeness, here is a link to someone else's question about trying to gauge endianness (at compile time): stackoverflow.com/questions/280162/…Tercet
Why not determine endianness at compile-time? It can't possibly change at runtime.Tambratamburlaine
AFAIK, there's no reliable and universal way to do that. gcc.gnu.org/ml/gcc-help/2007-07/msg00342.htmlVallery
You can't run the same code on Intel and PPC - you'll definitely have to compile separate binaries for each platform.Meek
A
183

I don't like the method based on type punning - it will often be warned against by compiler. That's exactly what unions are for!

bool is_big_endian(void)
{
    union {
        uint32_t i;
        char c[4];
    } bint = {0x01020304};

    return bint.c[0] == 1;
}

The principle is equivalent to the type case as suggested by others, but this is clearer - and according to C99, is guaranteed to be correct. GCC prefers this compared to the direct pointer cast.

This is also much better than fixing the endianness at compile time - for OSes which support multi-architecture (fat binary on Mac OS X for example), this will work for both ppc/i386, whereas it is very easy to mess things up otherwise.

Araucaria answered 16/6, 2009 at 13:8 Comment(18)
I don't recommend naming a variable "bint" :)Jose
are you sure this is well defined? In C++ only one member of the union can be active at one time - i.e you can not assign using one member-name and read using another (although there is an exception for layout compatible structs)Tercet
It is well defined in C99 AFAIK. On older platforms, it is implementation-dependent. But so is type punning through pointer cast, which is not defined in C99 either.Araucaria
@Matt: I looked into Google, and bint seems to have a meaning in English that I was not aware of :)Araucaria
@Faisal: In C++ only one member is guaranteed to work at a time, but in practice all compilers implement the extension that you can read from unions "as if" they had been assigned to with the value having the same storage representation that the value you actually assigned has. Assuming of course that the type you read does have a value with that storage representation. Certainly if all the questioner cares about is intel and PPC, and he's using normal compilers, then this is fine.Palpable
@David, @Matt: "two cultures separated by a common language" seems apropos here...Caddy
I've tested this, and in both gcc 4.0.1 and gcc 4.4.1 the result of this function can be determined at compile time and treated as a constant. This means that the compiler will drop if branches that depend solely on the result of this function and will never be taken on the platform in question. This is likely not true of many implementations of htonl.Elver
@Elver Hi, what do u mean by 'This means that the compiler will drop if branches that depend solely on the result of this function and will never be taken on the platform in question. ..'. ? Is this function still working?Impuissant
@user1559625: Yes. It's never even called. The compiler figures out the result of calling it at compile time and hardcodes that result into the resulting program. In fact, if it notices that this causes dead code because a certain path isn't taken, it drops the dead code. All this happens at compile time. It's optimization the compiler is doing. And the program is still just as correct.Elver
I don't understand how this works. How will the first byte be 1?Towline
@0x499602D2 0x01020304, the last digit is 4(hex)=100(binary).Infallible
using unions like that is completely valid, even if non-standard. Both GCC and VS guarantee that accessing parts of a value is okay. union means same memory address, different access methods, so union{ int i; char c[4];} girl; means that setting girl.c[0] will affect girl.i, BUT the "undefinedness" of behavior is, WHICH BYTE does c[0] corresponds of i? :} And this is endianess part, that is left undefined, but every game developer is aware and uses this.Syndicate
just need uint16_t to differ between small endian and big endian thoughDeliberation
Is this solution really portable? What if CHAR_BIT != 8 ?Administration
@Administration makes a good point. You should at least use uint8_t instead of char.Diocletian
This only works in C (by Standard), and is UB in C++. GNUC++ does support it though.Mozzarella
Wait a minute, isn't this technically undefined behaviour because you're reading from a union field that wasn't the last field assigned to? stackoverflow.com/a/11996970Macromolecule
@Пет: Another word for "non-standard" is "invalid". That's the opposite of "completely valid". The code, as posted, exhibits undefined behavior. It is reading from a union member that is not the active member. std::bit_cast was introduced, in part, so that we no longer are tempted to write code as recommended in this answer.Exultation
F
115

You can use std::endian if you have access to a C++20 compiler, such as GCC 8+ or Clang 7+.

Note: std::endian began in <type_traits>, but it was moved to <bit> at the 2019 Cologne meeting. GCC 8, Clang 7, 8 and 9 have it in <type_traits> while GCC 9+ and Clang 10+ have it in <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
}
Fortress answered 1/7, 2016 at 9:11 Comment(4)
As everyone I have access to C++17 and 20 drafts/proposals, but, as of now, does any C++20 compiler ever exist?Grooved
@Grooved It only requires scoped enumerations so I suspect most vendors will add it to their stdlib implementation as one of their earlier changes.Macromolecule
@Grooved GCC 8 was released and supports it.Fortress
Out of the 30+ answers to the question, this appears to be the only one, that's completely accurate (with another answer that's at least correct in part).Exultation
H
89

You can do it by setting an int and masking off bits, but probably the easiest way is just to use the built in network byte conversion ops (since network byte order is always big endian).

if ( htonl(47) == 47 ) {
  // Big endian
} else {
  // Little endian.
}

Bit fiddling could be faster, but this way is simple, straightforward and pretty impossible to mess up.

Houseboy answered 16/6, 2009 at 13:0 Comment(6)
The network conversion ops can also be used to convert everything to big endian, thus solving other problems Jay may be encountering.Elagabalus
Care should be taken - htonl implementation can be slow - its speed needs to be measured so its misuse doesn't introduce a bottleneck.Taryn
@Taryn - slow is a relative term, but yes, if speed is really an issue, use it once at the start of the program and set a global variable with the endianness.Houseboy
htonl has another problem: on some platforms (windows ?), it does not reside in the C runtime library proper, but in additional, network related libraries (socket, etc...). This is quite an hindrance for just one function if you don't need the library otherwise.Araucaria
Note that on Linux (gcc), htonl is subject to constant folding at compile time, so an expression of this form has no runtime overhead at all (ie it is constant-folded to 1 or 0, and then dead-code elimination removes the other branch of the if)Interrupter
Also, on x86 htonl can be (and is, on Linux/gcc) implemented very efficiently using inline assembler, particularly if you target a micro-architecture with support for the BSWAP operation.Interrupter
D
73

Please see this article:

Here is some code to determine what is the type of your machine

int num = 1;
if(*(char *)&num == 1)
{
    printf("\nLittle-Endian\n");
}
else
{
    printf("Big-Endian\n");
}
Desiccator answered 16/6, 2009 at 13:0 Comment(16)
Bear in mind that it depends on int and char being different lengths, which is almost always the case but not guaranteed.Scrobiculate
@David - very true but I would be surprised to learn of any architecture that would have ints and chars be the same size. Still, it is an important point to never make assumptions about stuff like this.Desiccator
Does this method rely on the fact that the code is always compiled on the same architecture?Stirrup
I've worked on embedded systems where short int and char were the same size... I can't remember if regular int was also that size (2 bytes) or not.Almemar
@Janusz: Yes and no. This will work on any architecture in which an int and a char are are different sizes. If you are concerned about or know of any architecture where this might be the case then this solution would not work for you.Desiccator
An example of identical ints and chars: leo.sprossenwanne.at/dsp/Entwicklungsprogramme/Entpack/CC56/DSP/…Fortunio
@Andrew Hare: I want to test this code on a Big-endian machine. I dont have one. Codepad and Ideone are both Little Endian. Do you know some way?Metzger
If you're using gcc, you can use typeof to ensure size issues don't get in the way: typeof(1L) num = 1; if( *(char*)&num == 1L ) {...} I just tested on Solaris/sparc with gcc 3.4.3, and Linux/x86 with GCC 4.4.5. Note, setting -std=c99 with this generates an error since typeof is not part of C99.Seena
why is THIS answer pretty much THE ONLY ANSWER that is NOT making me think "dude, wtf are you doing?", which is the case of most of the answers here :oDeliberation
@David @Andrew Hare I found this C standard from 2007 (open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf) which states that the min/max values for an int must be at least -(2^15 - 1) & 2^15-1, respectively (pdf page 34). So for any system implementing the standard from about the past decade onwards (and possibly further), it should be guaranteed that sizeof(int)>sizeof(char). (Any system that doesn't would have to be pretty dated.)Ayesha
@Shillard int must be at least that large, but there is no requirement in the standard for char being restricted to less! If you have a look at TI F280x family, you will discover that CHAR_BIT is 16 and sizeof(int) == sizeof(char) while the limits you mention are kept absolutely fine...Melanymelaphyre
Why not use uint8_t and uint16_t?Hilde
@DavidThornley if on your platform sizeof(int)==sizeof(char), you don't have endianness issues at all.Mozzarella
@Mozzarella until someone decides to use int16_tExigible
@Exigible in that case CHAR_BIT==16 (being bounded from above by the existence of int16_t and from below by range requirements for int), and you don't have an issue too.Mozzarella
Here's an illustration explaining how this works.Siloxane
D
45

This is normally done at compile time (specially for performance reason) by using the header files available from the compiler or create your own. On Linux you have the header file "/usr/include/endian.h".

Digraph answered 16/6, 2009 at 13:36 Comment(4)
I can't believe this hasn't been voted higher. It's not like the endianness is going to change under a compiled program, so there's never any need for a runtime test.Bunche
@Bunche It potentially could, see the ARM endian modes.Archeology
@Tyzoid: No, a compiled program will always run under the endian mode it was compiled for, even if the processor is capable of either.Bunche
This seems to be Linux only: man7.org/linux/man-pages/man3/endian.3.htmlHillell
M
25

Do not use a union!

C++ does not permit type punning via unions!
Reading from a union field that was not the last field written to is undefined behaviour!
Many compilers support doing so as an extension, but the language makes no guarantee.

See this answer for more details:

https://stackoverflow.com/a/11996970


There are only two valid answers that are guaranteed to be portable.

The first answer, if you have access to a system that supports C++20,
is to use std::endian from the <bit> header.

C++20 Onwards

constexpr bool is_little_endian = (std::endian::native == std::endian::little);
constexpr bool is_big_endian = (std::endian::native == std::endian::big);

Prior to C++20, the only valid answer is to store an integer and then inspect its first byte through type punning. Unlike the use of unions, this is expressly allowed by C++'s type system.

It's also important to remember that for optimum portability static_cast should be used, because reinterpret_cast is implementation defined.

If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined: [...] a char or unsigned char type.

C++11 Onwards

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 Onwards (with bool instead of enum class)

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);
}
Macromolecule answered 17/5, 2019 at 17:56 Comment(5)
Pretty sure your code would fail on targets with sizeof (int) == 1 which was at least in past allowed for C++... :D not that you'd need endianess checks there.Millen
"Reading from a union field that was not the last field written to is undefined behaviour!" Except for the common initial sequence.Adoptive
@Adoptive Which is irrelevant here because int and arrays of char or unsigned char do not share a common initial sequence.Macromolecule
The statement is missing out on context and can be quite misleading, e.g. when linking to this answer. To make it more clear, add a reference to the union solution.Adoptive
@Adoptive In what way is it misleading? The answer says quite clearly that using a union to solve the problem relies on either undefined behaviour or non-standard compiler extensions, which is correct. If people want an example of misusing a union to solve the problem, there are plenty of other answers that demonstrate that.Macromolecule
S
19

I surprised no one has mentioned the macros which the pre-processor defines by default. While these will vary depending on your platform; they are much cleaner than having to write your own endian-check.

For example; if we look at the built-in macros which GCC defines (on an x86-64 machine):

:| gcc -dM -E -x c - | grep -i endian

#define __LITTLE_ENDIAN__ 1

On a PPC machine I get:

:| gcc -dM -E -x c - | grep -i endian

#define __BIG_ENDIAN__ 1
#define _BIG_ENDIAN 1

(The :| gcc -dM -E -x c - magic prints out all built-in macros.)

Showbread answered 20/6, 2009 at 19:15 Comment(1)
These macros do not show up consistently at all. For example, in gcc 4.4.5 from the Redhat 6 repo, running echo "\n" | gcc -x c -E -dM - |& grep -i 'endian' returns nothing, whereas gcc 3.4.3 (from /usr/sfw/bin anyway) in Solaris has a definition along these lines. I've seen similar issues on VxWorks Tornado (gcc 2.95) -vs- VxWorks Workbench (gcc 3.4.4).Seena
R
16

Ehm... It surprises me that no one has realized that the compiler will simply optimize the test out, and will put a fixed result as return value. This renders all code examples in the previous answers effectively useless.

The only thing that would be returned is the endianness at compile-time! And yes, I tested all of the examples in previous answer. Here's an example with Microsoft Visual C++ 9.0 (Visual Studio 2008).

Pure C code

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

Disassembly

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

Perhaps it is possible to turn off any compile-time optimization for just this function, but I don't know. Otherwise it's maybe possible to hardcode it in assembly, although that's not portable. And even then even that might get optimized out. It makes me think I need some really crappy assembler, implement the same code for all existing CPUs/instruction sets, and well.... never mind.

Also, someone here said that endianness does not change during run-time. wrong. There are bi-endian machines out there. Their endianness can vary during execution. Also, there's not only little-endian and big-endian, but also other endiannesses.

Rubidium answered 4/5, 2011 at 1:11 Comment(6)
Don't you have to recompile to run on a different platform anyway?User
Although it works well for MSVC, it doesn't for all GCC version in all circumstances. Hence, a "run-time check" inside a critical loop may be correctly un-branched at compile-time, or not. There's no 100% guarantee.Anatollo
@User Not necessarily, I could compile on a weird version of Ubuntu for a big-endian x86 processor and then put the program on Ubuntu for a little-endian x86 processor. Them both being x86 means the byte code is the same to the processor and as such, integers will be interpereted differently.Carp
There is no such thing as a big-endian x86 processor. Even if you run Ubuntu on a biendian processor (like ARM or MIPS) the ELF executables are always either big (MSB) or little (LSB) endian. No biendian executables can be created so no runtime checks are needed.Peccable
To turn off the optimization in this method use 'volatile union ...' It tells compiler that 'u' can be changed somewhere else and data should be loadedBanka
For this function to return a different value at runtime than the optimizer is calculating that it will implies that the optimizer is bugged. Are you saying that there are examples of compiled optimized binary code that may portably run on two different architectures of different endianness, despite obvious assumptions made by the optimizer (throughout the program) during compilation that would seem to be incompatible with at least one of those architectures?Allred
T
15

Declare an int variable:

int variable = 0xFF;

Now use char* pointers to various parts of it and check what is in those parts.

char* startPart = reinterpret_cast<char*>( &variable );
char* endPart = reinterpret_cast<char*>( &variable ) + sizeof( int ) - 1;

Depending on which one points to 0xFF byte now you can detect endianness. This requires sizeof( int ) > sizeof( char ), but it's definitely true for the discussed platforms.

Taryn answered 16/6, 2009 at 13:0 Comment(0)
H
7

The C++ way has been to use Boost, where the preprocessor checks and casts are compartmentalized away inside very thoroughly-tested libraries.

The Predef Library (boost/predef.h) recognizes four different kinds of endianness.

The Endian Library was planned to be submitted to the C++ standard and supports a wide variety of operations on endian-sensitive data.

As stated in previous answers, Endianness will be a part of C++20.

Honorable answered 16/6, 2009 at 12:57 Comment(0)
P
7

Unless you're using a framework that has been ported to PPC and Intel processors, you will have to do conditional compiles, since PPC and Intel platforms have completely different hardware architectures, pipelines, busses, etc. This renders the assembly code completely different between the two.

As for finding endianness, do the following:

short temp = 0x1234;
char* tempChar = (char*)&temp;

You will either get tempChar to be 0x12 or 0x34, from which you will know the endianness.

Permanence answered 16/6, 2009 at 13:0 Comment(3)
This relies on short being exactly 2 bytes which is not guaranteed.Taryn
It'd be a pretty safe bet though based on the two architectures given in the question though.Michaelamichaele
Include stdint.h and use int16_t to future proof against short being different on another platform.Ringleader
S
7

For further details, you may want to check out this codeproject article Basic concepts on Endianness:

How to dynamically test for the Endian type at run time?

As explained in Computer Animation FAQ, you can use the following function to see if your code is running on a Little- or Big-Endian system: Collapse

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

This code assigns the value 0001h to a 16-bit integer. A char pointer is then assigned to point at the first (least-significant) byte of the integer value. If the first byte of the integer is 0x01h, then the system is Little-Endian (the 0x01h is in the lowest, or least-significant, address). If it is 0x00h then the system is Big-Endian.

Sporty answered 16/6, 2009 at 13:3 Comment(1)
That code makes several assumptions that need not be true: First - this function can and normally will be checked at compile-time only, so the result does not depend on the running architecture but only the compiling one. 2nd - this assumes that a 'short int' is 16 bit and a 'char' is 8 bit. NEITHER of which is guaranteed by the standard. They can even both be 64 bit.Unpleasant
C
6

As stated in previous answers, use union tricks.

There are a few problems with the ones advised above though. Most notably that unaligned memory access is notoriously slow for most architectures, and some compilers won't even recognize such constant predicates at all, unless word aligned.

Because a mere endian test is boring, here goes a (template) function which will flip the input/output of an arbitrary integer according to your specification, regardless of host architecture.

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

Usage:

To convert from given endian to host, use:

host = endian(source, endian_of_source)

To convert from host endian to given endian, use:

output = endian(hostsource, endian_you_want_to_output)

The resulting code is as fast as writing hand assembly on Clang, and on GCC it's tad slower (unrolled &,<<,>>,| for every byte), but it is still decent.

Cilia answered 12/10, 2012 at 21:22 Comment(0)
E
4

I would do something like this:

bool isBigEndian() {
    static unsigned long x(1);
    static bool result(reinterpret_cast<unsigned char*>(&x)[0] == 0);
    return result;
}

Along these lines, you would get a time efficient function that only does the calculation once.

Ecklund answered 16/6, 2009 at 13:6 Comment(1)
can you inline it? not sure if inline cause multiple memory blocks of the static variablesMisguide
S
4
bool isBigEndian()
{
    static const uint16_t m_endianCheck(0x00ff);
    return ( *((const uint8_t*)&m_endianCheck) == 0x0); 
}
Scrubland answered 25/11, 2012 at 15:58 Comment(1)
Would this be equivalent? #define IS_BIGENDIAN() (*((char*) &((int){ 0x00ff })) == (0x00))Nailbiting
P
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");

This is another solution. Similar to Andrew Hare's solution.

Phytohormone answered 2/10, 2012 at 10:10 Comment(0)
M
3

Declare:

Non-macro, C++11 solution:

union {
  uint16_t s;
  unsigned char c[2];
} constexpr static  d {1};

constexpr bool is_little_endian() {
  return d.c[0] == 1;
}
Malloy answered 21/5, 2014 at 4:43 Comment(8)
Is there a particular reason you used unsigned char over uint8_t?Sanctified
0 runtime overhead... i like it!Deliberation
I guess, this detects endiannes of the build machine, not the target?Weatherproof
Isn't this UB in C++?Botts
this is not legal in constexpr context. You can't access a member of a union that has not been initialised directly. There is no way to legally detect endianness at compile time without preprocessor magic.Dania
@Richard Hodges Thanks, you direct me to rethink this topic, I checked my code snippet with clang 6, there's a warning about the constexpr function: error: constexpr function never produces a constant expression [-Winvalid-constexpr], at the moment I think preprocessor is the only way to get a compile time(actually preprocessor happens before compile), zero cost method to know whether the target system is big or little endian.Malloy
@Deliberation sorry, the constexpr is misleading, although it may pass gcc, it not always means "const" or zero cost.Malloy
clang says: constexpr function never produces a constant expression [-Winvalid-constexpr]Gardol
D
3

This is untested, but in my mind, this should work. Because it'll be 0x01 on little-endian, and 0x00 on big-endian.

bool runtimeIsLittleEndian(void)
{
    volatile uint16_t i=1;
    return ((uint8_t*)&i)[0]==0x01; // 0x01=little, 0x00=big
}
Deliberation answered 14/2, 2015 at 3:10 Comment(0)
N
3

If you don't want conditional compilation you can just write endian independent code. Here is an example (taken from Rob Pike):

Reading an integer stored in little-endian on disk, in an endian independent manner:

i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);

The same code, trying to take into account the machine endianness:

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
Nonet answered 17/2, 2017 at 11:58 Comment(1)
Thanks a lot for this however I noticed I had to reverse it for it to work (I'm on a little endian machine (Intel corei3 9100) which was weird based on the link you provided. so for me (data[0]<<24) | (data[1]<<16) | (data[2]<<8) | (data[3]); worked!Inference
W
2

You can also do this via the preprocessor using something like a Boost header file which can be found in Boost endian.

Wishful answered 16/6, 2009 at 14:44 Comment(0)
B
2

Unless the endian header is GCC-only, it provides macros you can use.

#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!");
...
Bewick answered 18/4, 2015 at 19:8 Comment(2)
Aren't these __BYTE_ORDER__, __ORDER_LITTLE_ENDIAN__ and __ORDER_BIG_ENDIAN__?Grooved
@Xeverous: on my current platform, which is Android NDK, the names in the answer are the correct ones.Mayence
I
1

See Endianness - C-Level Code illustration.

// 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;
} 
Ilan answered 16/6, 2009 at 13:0 Comment(0)
K
0
int i=1;
char *c=(char*)&i;
bool littleendian=c;
Kinakinabalu answered 16/6, 2009 at 13:1 Comment(0)
C
0

Here's another C version. It defines a macro called wicked_cast() for inline type punning via C99 union literals and the non-standard __typeof__ operator.

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

If integers are single-byte values, endianness makes no sense and a compile-time error will be generated.

Changeling answered 16/6, 2009 at 17:55 Comment(0)
P
0

The way C compilers (at least everyone I know of) work the endianness has to be decided at compile time. Even for biendian processors (like ARM and MIPS) you have to choose endianness at compile time.

Furthermore, the endianness is defined in all common file formats for executables (such as ELF). Although it is possible to craft a binary blob of biandian code (for some ARM server exploit maybe?) it probably has to be done in assembly.

Peccable answered 25/11, 2012 at 14:56 Comment(0)
B
0

a c++20 solution:

constexpr bool compare(auto const c, auto const ...a) noexcept
{
  return [&]<auto ...I>(std::index_sequence<I...>) noexcept
    {
      return ((std::uint8_t(c >> 8 * I) == a) && ...);
    }(std::make_index_sequence<sizeof...(a)>());
}

static constexpr auto is_big_endian_v{
  compare(std::uint32_t(0x01234567), 0x01, 0x23, 0x45, 0x67)
};

static constexpr auto is_little_endian_v{
  compare(std::uint32_t(0x01234567), 0x67, 0x45, 0x23, 0x01)
};

static constexpr auto is_pdp_endian_v{
  compare(std::uint32_t(0x01234567), 0x23, 0x01, 0x67, 0x45)
};

The task can be accomplished more easily, but for some reason the <bit> header file is not always present. Here's a demo.

Backset answered 10/8, 2022 at 11:50 Comment(2)
methinks the result could be wrong sometimes because of cross compilation. The host may have different endianess than the target.Backset
I'm pretty sure the C++ standard mandates that constexpr expressions have the same value as if they were not constexpr (except that they are constant expressions when executed as constexpr) so I don't see how cross-compilation could make it fail.Trometer
L
-1

How about this?

#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;
}
Lydgate answered 16/6, 2009 at 13:2 Comment(0)
B
-1

As pointed out by Coriiander, most (if not all) of these code here will be optimized away at compilation time, so the generated binaries won't check "endianness" at run time.

It has been observed that a given executable shouldn't run in two different byte orders, but I have no idea if that is always the case, and it seems like a hack to me checking at compilation time. So I coded this function:

#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 wasn't able to optimize this code, even though it does optimize the other code here away. I believe that is because I leave the "random" value that was allocated on the smaller byte memory as it was (at least seven of its bits), so the compiler can't know what that random value is and it doesn't optimize the function away.

I've also coded the function so that the check is only performed once, and the return value is stored for next tests.

Bracteole answered 28/9, 2014 at 8:46 Comment(4)
Why allocate 4 bytes to work on a 2-byte value? Why mask an indeterminate value with 0x7FE? Why use malloc() at all? that is wasteful. And _BE is a (albeit small) memory leak and a race condition waiting to happen, the benefits of caching the result dynamically are not worth the trouble. I would do something more like this instead: 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]); } Simple and effective, and much less work to perform at runtime.Signification
@RemyLebeau, the whole point of my answer was to produce a code that isn't optimized away by the compiler. Sure, your code is much simpler, but with optimizations turned on it will just become a constant boolean after compiled. As I stated on my answer, I don't actually know if there is some way to compile C code in a way that the same executable runs on both byte orders, and I was also curious to see if I could make the check at runtime despite optimizations being on.Bracteole
@TexKiller then why not simply disable optimizations for the code? Using volatile, or #pragma, etc.Signification
@RemyLebeau, I didn't know those keywords at the time, and I just took it as a little challenge to prevent the compiler optimization with what I knew.Bracteole
C
-2

I was going through the textbook Computer System: a programmer's perspective, and there is a problem to determine which endian this is by a C program.

I used the feature of the pointer to do that as following:

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

As the int takes up four bytes, and char takes up only one byte. We could use a char pointer to point to the int with value 1. Thus if the computer is little-endian, the char that char pointer points to is with value 1, otherwise, its value should be 0.

Clishmaclaver answered 15/10, 2013 at 11:41 Comment(3)
this would be improved by using int32t.Ronel
^ if you want to nitpick, the best here is int16_fast_t . and @Archimedes520's current code won't work on an arch where int is natively int8 ;) (that might go against the c standards in the first place, though)Deliberation
How do you know an int takes up four bytes?Ruskin

© 2022 - 2024 — McMap. All rights reserved.