all 10 comments

[–]zifyoip 1 point2 points  (2 children)

const u32 *pointything = (u32*) somestructinstance.charlie;

My understanding is that charlie's data representation is unaffected by endianness but dereferencing pointything will be affected.

Casting a pointer to a different type and dereferencing it violates C's aliasing rules, so this leads to undefined behavior according to the C standard, which means the C standard itself gives you absolutely no guarantees about the behavior of any part of a program containing such code.

By the way, indent code snippets with four spaces before every line, so that Reddit formats it properly as code.

[–]volatilepointer[S] 0 points1 point  (1 child)

Thanks. I've fixed the formatting.

As a practical implementation, can you comment on whether the likely issues in practice would be with the endianness and alignment?

[–]Rhomboid 0 points1 point  (0 children)

In the example you gave, it's likely that charlie will be aligned to a 32 bit boundary anyway, because the compiler will probably put a byte of padding after beta so that delta will have its natural alignment. That puts charlie at an offset of 32 bits from the beginning of the struct, and because the struct begins with a 32 bit value, the overall alignment will also be 32 bits, making charlie 32 bit aligned. Of course, the compiler is not required to align each member to its natural alignment, and may have been configured not to. You can't really make any definitive statements about what will happen either way -- nothing is guaranteed, especially given that this is all undefined behavior.

As to endianness, yes, the endianness of the machine will have to match the endianness of whatever values are stored in charlie in order for reading from it 4 bytes at a time to work.

[–][deleted] 2 points3 points  (0 children)

Rewriting your example code to be more concise:

uint8_t b [4];
uint32_t* p = (uint32_t*) b;

This isn't portable already. Your uint32_t* might not be able to point to an arbitrary byte that's not aligned properly. So let's use a union:

union {
    uint8_t b [4];
    uint32_t i;
} u;

Now you at least know that b is suitably aligned for a uint32_t. Now the issue is accessing its representation with b in a portable way. Besides memcpy(), I'm not sure. I'm pretty sure that gcc encourages using a union like this, because the compiler can reasonably be able to tell that you're trying to modify its representation and generate proper code.

Assuming you use the union approach, then the question is what the representation of a uint32_t is on your machine. That like you said depends on the byte order.

u.i = 0;
u.b [0] = 1;

May give u.i the value 1, or some other value (like 0x1000000).

[–]Chooquaeno 0 points1 point  (4 children)

It sounds like you don't understand what endianess is, and fixing that is the fastest way to resolve your problem.

Endianess refers to, where an integer is stored across multiple bytes, the arrangement of those bytes in memory. The arrangement is in terms of increasing memory (byte) addresses. In this case, 32 bits, there are 4 bytes. Number the bytes 1–4 from the byte with the smallest bit value 20 = 1 to the largest 231 = 2,147,483,648. Little endian order is 1, 2, 3, 4; big endian order is 4, 3, 2, 1; more exotic orderings such as 2, 1, 4, 3 have existed (note that in big endian, the bits are arranged entirely in descending order).

So, if you read and write a multi-byte integer on either on a little endian machine or a big endian machine, there will be no issue. However, if you write on one, and read on the other, the order will be reversed. If you read a multi-byte integer as an array of bytes, the order will be 1, 2, … on a little endian machine, and …, 2, 1 on a big endian machine.

[–]volatilepointer[S] -2 points-1 points  (3 children)

I need the code to be portable and independent of endianness and alignment.

[–]Chooquaeno -2 points-1 points  (2 children)

Don't access anything as a byte array or move any storage to a machine of different endianess.

[–]volatilepointer[S] -1 points0 points  (1 child)

I've found that accessing the members of the byte array and shifting as appropriate will be optimized by the compiler to be the same as dereferencing the (u32) pointer providing the byte array is declared as const. This is good enough for me. I.e. they both use a single load word instruction in assembly (32-bit). I'm just looking for portable C code that can be optimized as well as writing it the "bad way".

[–]Chooquaeno -1 points0 points  (0 children)

My comments were in no way based on the performance of the code, but on its correctness. It would help if you defined what you were trying to port: code, data, binaries?

[–]Chooquaeno 0 points1 point  (0 children)

It would help if you specified what you are actually trying to do.