Matoe Matoe - 1 month ago 8
C Question

Why is pointer arithmetic inconsistent across architectures?

Given the following code:

unsigned char *packet_data = (unsigned char *)malloc(7);
memset(packet_data, 0, 7);

uint16_t crc = 0xa8a9;

*((uint16_t *)&packet_data[5]) = (crc >> 8) | (crc << 8);

for (int i = 0; i < 7; i++) {
printf("%02X ", packet_data[i]);
}
printf("\n");


On my Mac (x86_64), the output is, as expected, 00 00 00 00 00 A8 A9 (A8 goes in byte 5 (counting from 0)). Compiled with clang (LLVM 7.3.0)

On an armv5tejl machine, the output is, however, 00 00 00 00 A8 A9 00 (A8 goes in byte 4 (counting from 0)). In this case, if we switch
5
to
4
on the source code the exact same output will come out. Compiled with gcc 4.6.3, like this on the Godbolt compiler explorer.

Both machines are little endian.

Why does this happen?

Answer

Assuming alignment requirement for the type uint16_t is 2, then your code has undefined behavior1, as this pointer: (uint16_t *)&packet_data[5] isn't correctly aligned.

If you use an aligned offset, like 4, the results should be the same as the behavior is defined.


1 (Quoted from: ISO/IEC 9899:201x 6.3.2.3 Pointers 7):
A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined.