以下功能旨在检查指针b
是否指向数组a[0],...,a[len-1]
?
bool between(int *a, int len, int *b)
{
return (a <= b) && (b < a + len);
}
通过读取https://en.cppreference.com/w/c/language/operator_comparison,该函数将调用未定义的行为。如果是这样,正确的方法是什么?为什么标准不允许这样做?
答案 0 :(得分:8)
使用比较运算符<
,<=
,>
和{{比较两个不相关的指针(即不指向同一数组对象或结构的成员的指针) 1}}实际上确实会调用未定义的行为,如链接页面以及C standard的6.5.8节中所述。
关于为什么不允许这样做,并非所有实现都具有平面内存模型,无关对象不必位于执行比较有意义的内存区域。
因此,在>=
指向b
的成员的情况下,您的函数将返回true,在指向a
的最后一个成员的情况下将返回false,否则将调用未定义的行为
但是允许使用a
或==
比较不相关的指针。因此,您可以通过遍历数组并使用相等运算符将目标指针与每个元素进行比较来规避比较运算符的限制:
!=
虽然这不是一种有效的远程检查,但这是唯一的兼容方法。
当然,最好以不需要进行比较的方式构造程序。允许指向数组内部的指针落在数组外部的程序已经在调用未定义的行为,因此进行修复将消除对此类函数的需要。
但是请注意,允许 将指针增加到数组末尾的一个元素,并对该指针进行比较(尽管不能取消引用)。
答案 1 :(得分:0)
除了@dbush很好的答案之外,代码还可以在数组a
上进行数学运算,以获得更快的肯定结果。
首先尝试进行二进制搜索,如果between()
的结果为true
,并且指针到整数的映射是常见的实现,则true
的值将在O中找到(log(n))时间。
如果通过二进制搜索未在数组中找到b
,则仍需要使用线性来处理指针到整数的异常映射。
对于大型数组,例如n=1,000,000
,执行bsearch
的log2(1,000,000)步骤无关紧要。对于小型阵列,最好直接进入循环。
// 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;
}