You're right, using uintptr_t for pointer arithmetic is a bad idea.
I've worked on systems (Cray vector machines) where converting a pointer to an integer type, performing arithmetic on the integer, and converting back to a pointer could yield meaningless results. (Hardware addresses were 64-bit addresses of 64-bit words; byte pointers were implemented by storing a 3-bit offset in the high-order bits of the pointer.)
Pointer arithmetic works. It causes undefined behavior if it goes outside the address range of the enclosing object, but in those cases converting to uintptr_t and performing arithmetic will give you garbage results anyway.
I've worked on systems (Cray vector machines) where converting a pointer to an integer type, performing arithmetic on the integer, and converting back to a pointer could yield meaningless results. (Hardware addresses were 64-bit addresses of 64-bit words; byte pointers were implemented by storing a 3-bit offset in the high-order bits of the pointer.)
Pointer arithmetic works. It causes undefined behavior if it goes outside the address range of the enclosing object, but in those cases converting to uintptr_t and performing arithmetic will give you garbage results anyway.