Assuming alignment
is a uintptr_t
power of 2, looking for the next properly aligned address can be done using this expression:
(address + alignment - 1u) & ~(alignment - 1u)
This is used in custom memory allocators.
However, in order to perform this operation, the address needs to be cast to uintptr_t
, and the result needs to be cast back to a pointer.
Do C and C++ differ with respect to the legality and correctness of using such an expression?
Do C and C++ differ with respect to the legality and correctness of using such an expression?
In both C and C++ the semantics of your code are implementation-defined.
The only use of a pointer->integer cast that is guaranteed in C++ to work is to cast the integer result back to the original pointer type, in which case it produces the original pointer value. (Note that "value" here includes pointer provenance. If there are multiple objects at the address or if there is an object ending at the address immediately before the casted one, then there is no guarantee that the cast can be used to obtain a pointer to a different object than it originally pointed to or one-past.)
In C, there are no guarantees made about the result of the cast at all, except for casting null pointer constants (i.e. 0
) to pointers (but only in that direction, not the the other!). In C the existence of uintptr_t
guarantees that a cast from a pointer to it at least doesn't have undefined behavior or results in a trap representation. Otherwise not even that would be excluded.
C++ as std::align
for this purpose which is not relying on the implementation-defined semantics. There is no portable equivalent for C as far as I am aware.
Also note that even though in practice the calculation you show will work as expected, how to actually use the result in order to access memory by some type without UB or to compare the pointer result (after a cast) to other pointer values is a much more complex problem. Here C and C++ have differing object models and additionally there are pointer provenance issues to consider in both languages.
In particular, for C++, even with the portable result of std::align
it is not specified to point to any particular object. It is only returning a pointer value representing the desired address. In order to access through that pointer, generally a cast to the target pointer type and std::launder
is required if an object of the target type is already alive at the memory address or a placement-new if there isn't yet. (And even then there are reachability requirements for std::launder
to have defined behavior. For example this can't be used to access a complete object from a one-past-the-end pointer to an object located before that object in memory.) All of this would technically be the case even if std::align
is only used for arithmetic in a character buffer, although there might be an underspecification of the function here as well.