tags:

views:

243

answers:

3

I came across the following code, and was told that it means that COL_8888_RED is "endian independent". Why? What makes this endian independent? (I have asked the original coder but they're not getting back to me ... heck maybe they don't know either.)

union _colours {
    uint8  c[3][4];
    uint32 alignment;
};

static const union _colours col_8888 = {
        {    /*   B     G     R     A  in memory     */
                {    0x00, 0x00, 0xFF, 0xFF, }, /* red   */
                {    0x00, 0xFF, 0x00, 0xFF, }, /* green */
                {    0xFF, 0x00, 0x00, 0xFF, }, /* blue  */
        }
};

#define COL_8888_RED   *((uint32 *)&col_8888.c[0])
+10  A: 

This code is not "endian-independent" in a sense that platforms with different endianness will give you different values seen through COL_8888_RED. In other words, in the traditional understanding of endian-dependency this code is as endian-dependent as it ever gets.

A different question is where is that COL_8888_RED is supposed to be used. Maybe it is intended to be passed to some API, which by itself is endian-dependent in the very same way to the point that the endian-dependency of the API cancels out the endian-dependency of COL_8888_RED. In that case everything will work "as intended", i.e. endian-independently. (For example, if the API receives the color value as uint32 and then separates it into the ARGB components by using the same union, it will get the correct original ARGB values regardless of endianness.)

But nevertheless, to say that the value of COL_8888_RED by itself is endian-independent is totally incorrect.

AndreyT
@Andrey, would a better term be "endian agnostic" then?
BeeBand
@BeeBand, then we see the question "Why is this code endian agnostic". The same degree of figuring it out will go into any other term used.
maxwellb
@Andrey, `COL_8888_RED` is going to be written directly to an element in an array of `uint32`.
BeeBand
@maxwellb, true :)
BeeBand
@BeeBand: "Written directly to an element in an array". Great. And what next?
AndreyT
@AndreyT, the array of `uint32` belongs to a "canvas" object which then gets rendered to the screen via functions not visible to me, they are wrapped up in the libraries that I'm using.
BeeBand
@BeeBand: In that case, as I said in my answer, the endian-[in]dependency of the resultant code will depend on what these function are internally doing with that array. And that endian-[in]dependency conclusion will apply to the *combination* of what you posted and what is done inside those functions. But out of context, alone, by itself what you posted is explicitly and obviously endian-dependent.
AndreyT
+2  A: 

I suppose that COL_8888_RED, as a macro, will always be a uint32, which, as long as the macro COL_8888_RED is always used, the source byte array {0x00,0x00,0xFF,0xFF} will always translate into what the programmer wants to mean as RED.

The definition, then, means you can write the same source code on a big endian, or little endian, machine, and go from a discrete array to a logical colour.

EDIT: why then, not use an enumeration, or a constant like "1"?

Probably, the original API developer wanted to be able to point to another location of {0x00,0x00,0xFF,0xFF} in memory, so that code like the following could be written:

uint8 *p = malloc( sizeof(uint8)*4 );

fread( p, sizeof(uint8), 4, inBuff );

if( *((uint32 *)p) == COL_8888_RED )
{
    printf( "I read red!\n" );
}
maxwellb
FWIW, casting like this falls foul of strict-aliasing rules, which really can cause problems, especially with optimisation turned on.
Oli Charlesworth
+3  A: 

Arrays of bytes are endianness independent on almost all architectures. By assigning to it as a byte array, the programmer ensures consistent content for the data. The bytes can also be accessed individually on any architecture. If the coder had just made an array of 3 words, machine endianness would play a role in determining the exact layout of the bits.

Nathon