checking if pointer points within an array checking if pointer points within an array arrays arrays

checking if pointer points within an array


Although the comparison is valid only for pointers within the array and "one past the end", it is valid to use a set or map with a pointer as the key, which uses std::less<T*>

There was a big discussion on this way back in 1996 on comp.std.c++


Straight from the MSDN documentation:

Two pointers of different types cannot be compared unless:

  • One type is a class type derived from the other type.
  • At least one of the pointers is explicitly converted (cast) to type void *. (The other pointer is implicitly converted to type void * for the conversion.)

So a void* can be compared to anything else (including another void*). But will the comparison produce meaningful results?

If two pointers point to elements of the same array or to the element one beyond the end of the array, the pointer to the object with the higher subscript compares higher. Comparison of pointers is guaranteed valid only when the pointers refer to objects in the same array or to the location one past the end of the array.

Looks like not. If you don't already know that you are comparing items inside the array (or just past it), then the comparison is not guaranteed to be meaningful.

There is, however, a solution: The STL provides std::less<> and std::greater<>, which will work with any pointer type and will produce valid results in all cases:

if (std::less<T*>()(p, begin)) {    // p is out of bounds}

Update:

The answer to this question gives the same suggestion (std::less) and also quotes the standard.


The only correct way to do this is an approach like this.

template <typename T>bool points_within_array(T* p, T* begin, T* end){    for (; begin != end; ++begin)    {        if (p == begin)            return true;    }    return false;}

Fairly obviously, this doesn't work if T == void. I'm not sure whether two void* technically define a range or not. Certainly if you had Derived[n], it would be incorrect to say that (Base*)Derived, (Base*)(Derived + n) defined a valid range so I can't see it being valid to define a range with anything other than a pointer to the actual array element type.

The method below fails because it is unspecified what < returns if the two operands don't point to members of the same object or elements of the same array. (5.9 [expr.rel] / 2)

template <typename T>bool points_within_array(T* p, T* begin, T* end){    return !(p < begin) && (p < end);}

The method below fails because it is also unspecified what std::less<T*>::operator() returns if the two operands don't point to members of the same object or elements of the same array.

It is true that a std::less must be specialized for any pointer type to yield a total order if the built in < does not but this is only useful for uses such as providing a key for a set or map. It is not guaranteed that the total order won't interleave distinct arrays or objects together.

For example, on a segmented memory architecture the object offset could be used for < and as the most significant differentiator for std::less<T*> with the segment index being used to break ties. In such a system an element of one array could be ordered between the bounds of a second distinct array.

template <typename T>bool points_within_array(T* p, T* begin, T* end){    return !(std::less<T*>()(p, begin)) && (std::less<T*>()(p, end));}