Ok, just some more precisions and reformulations...
Actually I just realised its non-portable but I will get onto that.
This is what I was suspecting.
In the last line, in both cases, the arithmetic on the right hand side of the equation is all integer arithmetic so there is no problem with that, everything is operating on an integer and the result is a (relatively small) integer that is then used in pointer arithmetic to adjust the size of the pointer all seems fine.
I do agree.
But I said all seems fine and here is the kicker, the standard makes no requirements on the value of an integer that has got its value from casting from a pointer other than the value can be cast back to a similar pointer. Specifically the standard does not require that the value of the integer is the actual address of the memory location (although it would have to be related in some way) but if you imagine a system where the value of the integer representation of a pointer was not the actual memory address the arithmetic doesn't work.
I also agree and I would just add that with the standard there is no insurance that:
- We will get the same integer values when the same pointer is casted twice into uintptr_t; we only have the insurance that once casted back the pointers will compare equal (so hashing the resulting integer is platform specific).
- We will get a valid pointer if we perform any arithmetics on the resulting integer.
- Casting a pointer into an unsigned long (or unsigned long long) and into an uintptr_t will produce the same values (there maybe some platform specific computations done)
Heres a worked example, imagine for a moment a system that has 31 bit addresses and uses the least significant bit to store a status or trap value of some kind for the memory location with the most significant 31 bits used for the actual memory address. A memory location of 0x00001003 would have a pointer value of 0x0001003x (where x is the status bit) assuming a status of 0 and an alignment of 8 and the right side of the equation becomes
I think that we should have 0x0002006 + x = 0x1003 * 2 + x.
And consequently:
0x0002006 % 8 = 6
Nevertheless your demonstration is still relevant.
I have had a look to the way the memalign function is implemented in glibc2.11 and something similar to "
q -= ((uintptr_t) q) % alignment;" is done.
My guess is that using this method to align the memory should work on most of the platforms but is somewhat specific (and not so portable).