简体   繁体   中英

How to check whether a pointer is pointing to an element inside an array?

The following function intends to check whether the pointer b is pointing to the array a[0],...,a[len-1] ?

bool between(int *a, int len, int *b)
{
    return (a <= b) && (b < a + len);
}

From reading https://en.cppreference.com/w/c/language/operator_comparison the function invokes undefined behaviour. If so, what is the right way to do this? Why does the standard disallow this?

Comparing two unrelated pointers (ie pointers that don't point to members of the same array object or struct) using the comparison operators < , <= , > , and >= do indeed invoke undefined behavior as described in the linked page as well as section 6.5.8 of the C standard .

As to why this is disallowed, not all implementations have a flat memory model, and unrelated objects need not reside in areas of memory where performing a comparison makes sense.

So your function would return true in cases where b points to a member of a , false when it points one past the last member of a , and would invoke undefined behavior otherwise.

It is however allowed to compare unrelated pointers using == or != . So you can circumvent the limitation on comparison operators by looping through the array and using equality operators to compare the target pointer with each element:

bool between(int *a, int len, int *b)
{
    int i;
    for (i=0; i<len; i++) {
        if (a+i == b) {
            return true;
        }
    }
    return false;
}

While this is not a efficient as a ranged check, it is the only compliant way to do so.

Of course, it would be better to construct your program in such a way that such a comparison is not necessary. A program that is allowing a pointer to inside an array to fall outside of the array is already invoking undefined behavior, so fixing that would eliminate the need for such a function.

Note however that it is allowed to increment a pointer to one element past the end of an array and perform comparisons on that pointer (although it cannot be dereferenced).

In addition to @dbush good answer, code can do math on the array side a to achieve a potential quicker positive result.

By attempting a binary search first, if the outcome of between() is true and the mapping of pointers to integers are in a common implementation, the true value will be found in O(log(n)) time.

If b is not found in the array via the binary search, a linear is still needed to cope with unusual mapping of pointers to integers.

For large arrays, like n=1,000,000 the log2(1,000,000) steps of doing a bsearch is insignificant. For small arrays, best to proceed directly to the loop.

 // When optional, but common, uintptr_t available
int fcmp(const void *key, const void *array_element) {
  if (key == array_element) {
    return 0;
  }
  uintptr_t ai = (uintptr_t) array_element;
  uintptr_t ki = (uintptr_t) key;
  return (ai > ki) - (ai < ki);
}

// Best to use `size_t` for array indexing
bool between(int *a, size_t a_sz, int *b) {
  if (bsearch(b, a, a_sz, sizeof *a, fcmp)) {
    return true;
  }
  for (size_t i = 0; i < a_sz; i++) {
    if (a + i == b) {
      return true;
    }
  }
  return false;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM