How to check if a pointer points to a properly aligned memory location?
Asked Answered
P

1

44

Given a void * to some storage, how to check whether it points to properly aligned storage without any implementation defined behavior?

Of course we have std::align, but is there a more effective way to do this?

template <std::size_t alignment>
inline bool is_aligned(void * ptr) noexcept {
    std::size_t max = 1u;
    return std::align(alignment, 1u, ptr, max);
}

PS: I need to do this in a C++ standards-compatible fashion, without relying on any platform-specific (implementation defined) hacks.

PPS: I apologize for my (comprehension of) English, its not my native language.


EDIT (2018.08.24): Removed "effective" from the title, added even more wording to emphasize that I don't want any implementation defined or platform-specific behavior.

Prudhoe answered 7/2, 2017 at 15:14 Comment(15)
The important thing about pointers (in a case like this) is to remember that they are really only integer values corresponding to an address. It's the compilers treatment of them that makes them special. And since a pointer is just an integer you can use normal bitwise operators on them (with the proper casting of course, std::intptr_t is useful here). For example to check if a pointer is on an even address you could do reinterpret_cast<intptr_t>(some_pointer) & ~1.Steinbach
With that said, since ptr (in your example) is not itself a constexpr then neither can the result of any calculation using it be constexpr.Steinbach
Also note that alignment is by definition platform specific and operating on numerical values returned from a reinterpret_cast such as suggested by @Some is inherently undefined or implementation defined behaviour.Influent
Lastly, if you have allocated memory using new or new[] you can be certain that the memory should be well-aligned for the type you're working with. Perhaps you can enlighten us about the problem you are actually trying to solve by checking the alignment? Why do you need to check it?Steinbach
@Someprogrammerdude This can be useful when one has a buffer with arbitrary raw data, and one needs to check whether parts of it can be accessed directly as some type T or whether one needs to use std::memcpy to copy the respective data from/to the buffer before/after accessing these as type T.Prudhoe
@Someprogrammerdude before C++17 that's only true for types with standard alignment, it's not true for something like struct OveralignedInt { alignas(1024) int i; };Hayman
@Prudhoe It is UB to access arbitrary raw data (char buffer) as some T even when alignment requirements are met. You'll have to begin the lifetime of the object with placement new first.Kreplach
@jotik: How would you have such a buffer at compile time? Compile time C++ does not allow for that kind of trickery. So why would your detection function need to be constexpr?Nicknack
@yurikilochek Even for trivially copyable types?Prudhoe
@NicolBolas I wasn't sure, and that's why I used the word preferably.Prudhoe
@Prudhoe yes, even for thoseKreplach
@yurikilochek Even when its not a char buffer, but just storage allocated by, say, ::operator new(std::size_t)?Prudhoe
@Prudhoe yes, likewise for malloc. There is no object for you to access until you create it.Kreplach
Possible duplicate of How to determine if memory is aligned? @Atlaste's answer is C++ and seems to answer your question.Speedwriting
@Speedwriting Both of these rely on implementation-defined behavior. I edited my question to emphasize even more that I don't want to rely on implementation-defined behavior.Prudhoe
G
32

If the remainder isn't zero when dividing the address with the desired alignment, then the address isn't aligned.

inline bool
is_aligned(const void * ptr, std::uintptr_t alignment) noexcept {
    auto iptr = reinterpret_cast<std::uintptr_t>(ptr);
    return !(iptr % alignment);
}

Ths can't be constexpr though, because of the cast.

Also, this relies on the implementation-defined fact that the conversion from pointer to integer must preserve the numeric representation of the address. As pointed out by the comments, that is not guaranteed by the standard, so this function is not necessarily portable to all platforms. That is also true, because it is optional for the implementation to provide std::uintptr_t.


I would expect this to only be needed when aligning for a type, so this might be more convenient:

template<class T>
bool
is_aligned(const void * ptr) noexcept {
    auto iptr = reinterpret_cast<std::uintptr_t>(ptr);
    return !(iptr % alignof(T));
}
Ginni answered 7/2, 2017 at 15:40 Comment(18)
Any pointer may be converted to an unsigned integer at least the size of uintptr_t. However, the result is implementation-defined. You're assuming a common implementation is used and the memory model is flat, basically, which is very likely these days, but not guaranteed.Partake
@ArneVogel yeah, seems like this is not guaranteed to be strictly portable to obscure platforms, where the conversion might change the alignment.Ginni
You can make is_aligned constexpr like follows: template<typename T> inline constexpr bool is_aligned(const void *p) { return (reinterpret_cast<const uintptr_t>(p) % alignof(T)) == 0; }Bona
If you know you’re on a 2s complement machine, then & (alignof(T)-1) might be faster than % alignof(T), but I’d hope that the compiler would spot that itself.Bona
Arne Vogel: the presence of uintptr_t on a platform is probably a hint that such a conversion is a reasonable thing to do however.Bona
@PhilArmstrong 2's complement is not used for unsigned numbers, it has no relevance :) But yes, any decent optimizer will be able to do that transformation.Ginni
@PhilArmstrong - can you determine at compile-time whether std::uintptr_t exists, other than invoking a compiler error? E.g., via a macro or other such thing, in a way such that you can compile alternate code-paths depending on its existence.Barnett
@PhilArmstrong I tested your function, and also tested adding constexpr to my template. It works, to my surprise. I'm confused because I don't understand why reinterpret_cast can be used in a constexpr template, but not in a regular constexpr function. Can you explain / refer to the standard?Ginni
Point on the 2s complement! Regarding constexpr & templates. That’s a very good question! Might just be a compiler bug?Bona
clang-4.0 compiles my templated version happily as well, so it’s a fairly widespread error! (g++ 6.3 still accepts reinterpret_cast in ordinary constexpr functions by the looks of things, but clang++4 rejects them as the standard requires.)Bona
@Barnett You can use SFINAE to do it, via some icky testing for the existence of a destructor for uintptr_t: #10712452Bona
@PhilArmstrong : Constexpr function templates for which no template argument will satisfy all constexpr requirements are ill-formed, no diagnostic required. This is what both you and @user2079303 are running into.Haplosis
@Haplosis If it’s ill formed, then why isn’t the compiler complaining about it? SFINAE doesn’t apply if you’re actually instantiating the template in question!Bona
@Haplosis some rules are not diagnosable. Standard doesn't require a diagnostic when such rules are violated. This is one of those rules.Ginni
@PhilArmstrong : That's what 'no diagnostic required' means. See another of user2079303's answers, Ill-Formed, No Diagnostic Required (NDR): ConstExpr Function Throw in C++14Haplosis
K. I wasn’t aware that "ill-formed, no diagnostic required" was actual standard-ese. (Also, Ugh. What a horrible trap to set for programmers to walk into.)Bona
@ArneVogel to quote the standard (draft): typedef unsigned integer type uintptr_t; // optionalGinni
Oops, you're right, my bad! The only "official" guarantee (inherited from C99) is still safe round-trip conversion, however: "The following type designates an unsigned integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer:" (7.18.1.4)Partake

© 2022 - 2024 — McMap. All rights reserved.