Is it well-defined to hold a misaligned pointer, as long as you don't ever dereference it?

No, the new code still has undefined behaviour. C11 6.3.2.3p7:

  1. 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 68) for the referenced type, the behavior is undefined. [...]

It doesn't say anything about dereferencing the pointer - even the conversion has undefined behaviour.


Indeed, the modified code that you assume is ARM-safe might not be even Intel-safe. Compilers are known to generate code for Intel that can crash on unaligned access. While not in the linked case, it might just be that a clever compiler can take the conversion as a proof that the address is indeed aligned and use a specialized code for memcpy.


Alignment aside, your first excerpt also suffers from strict aliasing violation. C11 6.5p7:

  1. An object shall have its stored value accessed only by an lvalue expression that has one of the following types:88)
    • a type compatible with the effective type of the object,
    • a qualified version of a type compatible with the effective type of the object,
    • a type that is the signed or unsigned type corresponding to the effective type of the object,
    • a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
    • an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
    • a character type.

Since the array buf[2048] is statically typed, each element being char, and therefore the effective types of the elements are char; you may access the contents of the array only as characters, not as int32_ts.

I.e., even

int32_t nextWord = *((int32_t *) &buf[_Alignof(int32_t)]);

has undefined behaviour.


To safely parse multi-byte integer across compilers/platforms, you can extract each byte, and assemble them to integer according to the endian. For example, to read 4-byte integer from big-endian buffer:

uint8_t* buf = any address;

uint32_t val = 0;
uint32_t  b0 = buf[0];
uint32_t  b1 = buf[1];
uint32_t  b2 = buf[2];
uint32_t  b3 = buf[3];

val = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;

Some compilers may assume no pointer will ever hold a value that is not properly aligned for its type, and perform optimizations that rely upon that. As a simple example, consider:

void copy_uint32(uint32_t *dest, uint32_t *src)
{
  memcpy(dest, src, sizeof (uint32_t));
}

If both dest and src hold 32-bit aligned addresses, the above function could be optimized to one load and one store even in platforms that don't support unaligned accesses. If the function had been declared to accept arguments of type void*, however, such an optimization would not be allowed on platforms where unaligned 32-bit accesses would behave differently from a sequence of byte accesses, shifts, and bit-wise operations.