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.
union
rules, but not this one. – Earthworkalignas
keyword for aligning data. – Chinkianguintptr_t
, most C implementations provide a meaningful value that can be used to calculate an aligned address (in an implementation-specific way, not necessarily portable). However, using that address to create or access an object in that location is another matter involving considerably more details about language rules. Unless all you want is an address that is not usable for much, you need to specify what you want to do with it. – Evensonuintptr_t
. Yet math onuintptr_t
is not spec specified to form the equivalent with math on a pointer (even achar *
). The unposted larger code functionality is perhaps reasonable, yet implementation specific. – Cosmeticaddress
and an alignment requirement inalignment
, it will calculate the desired address, within the representable bounds of the type. None of that is a problem in writing a custom memory allocator… – Evensonuintptr_t
sufficiently specified, is the conversion fromuintptr_t
to a pointer sufficiently specified, and are the aliasing rules satisfied, in regarding to the client using the allocated memory and later releasing it the memory allocated, which may reuse it for other purposes? Those are the questions that are critical for implementing memory allocation routines. The alignment calculation is trivial. – Evensonmalloc
andfree
, using only strictly conforming C. It can only be done using implementation-specific characteristics, such as guarantees about the pointer conversions. A primary way the aliasing issues are resolved is by compiling the memory allocation routines separately from their clients. The C standard does not specify it, but aliasing issues cannot cross translation units that are kept semantically separate through compilation and linking. – Evenson