views:

4215

answers:

16

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).

+2  A: 

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.

samoz
This relies on short being exactly 2 bytes which is not guaranteed.
sharptooth
It'd be a pretty safe bet though based on the two architectures given in the question though.
Daemin
+7  A: 

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.

sharptooth
+11  A: 

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");
}
Andrew Hare
its easier than htonl stuff..we dont need any library ..cool
Warrior
Bear in mind that it depends on int and char being different lengths, which is almost always the case but not guaranteed.
David Thornley
@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.
Andrew Hare
Does this method rely on the fact that the code is always compiled on the same architecture?
Janusz
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.
rmeador
@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.
Andrew Hare
An example of identical ints and chars:http://leo.sprossenwanne.at/dsp/Entwicklungsprogramme/Entpack/CC56/DSP/INCLUDE/LIMITS.H
Dingo
@Andrew Hare: I want to test this code on a Big-endian machine. I dont have one. [Codepad](http://codepad.org/) and [Ideone](http://ideone.com/) are both Little Endian. Do you know some way?
Lazer
A: 

See Endianness - C-Level Code illustration.

// assuming target architecture is 32-bit = 4-Bytes
enum ENDIANESS{ LITTLEENDIAN , BIGENDIAN , UNHANDLE };


ENDIANESS 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;
}
gimel
+22  A: 

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.

Eric Petroelje
The network conversion ops can also be used to convert everything to big endian, thus solving other problems Jay may be encountering.
Brian
Care should be taken - htonl implementation can be slow - its speed needs to be measured so its misuse doesn't introduce a bottleneck.
sharptooth
@sharptooth - 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.
Eric Petroelje
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.
David Cournapeau
A: 
int i=1;
char *c=(char*)&i;
bool littleendian=c;
Jon Bright
+1  A: 

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;
}
Abhay
A: 

What operations are you planning on doing where endianness makes a difference? All standard arrithmetic, array manipulation etc. operations will be portable across the different architectures, as the differences will be taken care of for you by the different platform's compilers.

anon
Saving and loading files across differing-endian platforms comes to mind.
fbrereto
A: 
none
+1  A: 

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.

Jeremy Mayhew
+30  A: 

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 !

int 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 OS 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.

David Cournapeau
I don't recommend naming a variable "bint" :)
Matt Kane
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)
Faisal Vali
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.
David Cournapeau
@Matt: I looked into Google, and bint seems to have a meaning in English that I was not aware of :)
David Cournapeau
@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.
Steve Jessop
@David, @Matt: "two cultures separated by a common language" seems apropos here...
RBerteig
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.
Omnifarious
God Bless GCC™.
LiraNuna
+2  A: 

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"

bill
A: 

You can also do this via the preprocessor using something like boost header file which can be found boost endian

A: 

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.

Christoph
A: 

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).

Dave Rigby
A: 

Dave, the reason is that it is not standard (ISO 14882), don't expect it to be supported by every compiler. For example, it does not exist on the mingw version of gcc, nor on the Microsoft C++ compiler.