【问题标题】:In c++ how to check a pointer lies within a range?在 C++ 中如何检查一个指针是否在一个范围内?
【发布时间】:2020-09-24 08:22:20
【问题描述】:

直观地检查 whecker 指针 p 位于 [a,b) 就可以了

a<=p && p<b

但是,比较两个数组中的指针会导致未指定的行为,因此我们不能安全地说 p 在此比较中的 [a,b) 中。

有什么方法可以确定地检查这一点吗?

(如果可以为std::vector&lt;T&gt;::const_iterator做就更好了,但我觉得不可行。)

【问题讨论】:

  • 将指针重新解释为 uintptr_t 并使用它来比较地址,请参阅 devblogs.microsoft.com/oldnewthing/20170927-00/?p=97095 以获得更深入的信息
  • 你想达到什么目的?为什么要检查指针的范围?你为什么要使用原始指针?
  • “在一个范围内”是什么意思?指向与ab 指向相同数组的元素?
  • 没有“语言律师”的方式来做到这一点。 C++ 运行在此处建议的每种方法都将失败的系统上。 在特定平台上,您可能会成功地转换为 @UnholySheep 建议的整数。
  • 注意:其中一个检查复杂的系统是 x86 实模式,其中指针使用分段寻址。注意:对于任何使用虚拟地址转换的系统来说,它也很复杂(阅读:从用户模式来看几乎不可能)

标签: c++ pointers language-lawyer


【解决方案1】:

这是部分解决方案。您可以利用比较会调用未指定行为的事实,以及 core-constant-expression 无法执行此操作的事实:

template<typename T>
constexpr bool check(T *p, T *a, T *b)
{
    return a <= p and p < b;
}

现在这个函数可以这样使用:

int main()
{
    int arr[5];
    int arr_2[5];
    constexpr bool b1 = check(arr + 1, arr, arr + 3);    // ok
    constexpr bool b2 = check(arr_2 + 1, arr, arr + 3);  // error
}

这是demo

这显然只有在编译时知道指针值时才有效。在运行时,没有有效的方法来执行此检查。

【讨论】:

【解决方案2】:

指针的解决方案是使用&lt;functional&gt;中定义的比较对象,如less/less_equal

来自标准的§20.8.5/81

对于模板greaterlessgreater_equalless_equal,任何指针类型的特化都会产生一个总顺序,即使内置运算符&lt;&gt; , &lt;=, &gt;= 不要。

所以指针的解决方案是:

template<typename T>
bool check(T *p, T *a, T *b)
{
    return std::less_equal<T*>{}(a,p) && std::less<T*>{}(p,b);
}

这是一个使用指针的working example


迭代器没有这么严格的保证;但是这可以在 中解决,因为它提供了std::to_address,可以将可指向对象转换为指针。但是请注意,为了比较而执行此操作的行为仅对连续迭代器进行了很好的定义。

由于我们知道 std::vector 迭代器覆盖了一个连续的范围,我们可以使用它来检索底层指针(注意: not 取消引用它,因为这将是未定义的行为用于过去的指针)。

所以对于std::vector&lt;T&gt;::iterator,解决方案可能如下所示:

template <typename T>
bool check(const std::vector<T>::const_iterator p, std;:vector<T>::const_iterator a, std::vector<T>::const_iterator b)
{
    // Delegate to the pointer check version defined above, for brevity
    return check(std::to_address(p), std::to_address(a), std::to_address(b));
}

这是一个使用迭代器的working example


1同样的注释一直存在到,在§23.14.7/2下,措辞相似。

【讨论】:

  • check(p, a, b) == true,对于 ab 指向同一个数组的元素,并不能保证 p 指向这些元素之间的元素。
  • @LanguageLawyer 标准的链接部分保证功能对象将具有总排序比较。我不确定其中的哪一部分不清楚,但即使在您描述的场景中,总订单也是明确定义的。此外,OP 似乎在询问 p 是否来自不同的数组(否则将是未指定),而不是 ab 是不同的数组。这也是其他答案似乎解释这个问题的方式
  • 不清楚 OP 想要什么,但如果 OP 想要检查 p 是否指向与 ab 相同的数组的元素,std::less 将无济于事.
  • std::less 要求遵循&lt;&gt;&lt;=&lt;=&gt;= 对作为总排序一部分的指针施加的排序.因此,如果p[a,b) 中,则要求 std::less 继续工作。一个推论是,如果p 不是[a,b) 中的not,则结果必须继续完全排序——因此必须继续工作。即使 ab 是不同的数组,仍然会产生完全有序的结果——它只是不会是一个定义明确的结果,因为它依赖于实现细节
  • 我知道std::less 提供总订单。我想说即使a&lt;=p &amp;&amp; p&lt;b 是假的,check(p, a, b) 也可能是假的。
【解决方案3】:

如果我理解正确,您想检查向量迭代器是否位于其他两个向量迭代器之间。

然后您可以使用std::distance 来计算vector.begin 与a、p 与b 之间的距离,然后简单地比较您从distance 返回值得到的迭代器。

【讨论】:

  • 这行不通。如果p 不在ab 之间,则std::distance(a, p) 的行为未定义。你可以用它做任何有意义的事情。
  • @PeteBecker,我建议计算距离 .begin() - 这是未定义的吗?
  • a 来自哪里并不重要。算法适用于序列;如果p 不在[a,b) 定义的序列中,则不能调用std::distance(a, p);
【解决方案4】:

来自 C++17 的std::distance(first, last) 可以同时用于两者,但如果无法从first 访问last(例如不同的范围或无效的迭代器),则结果未定义

【讨论】:

  • 所以这可以给你ab之间的距离。但是如果p 不在[a;b) 数组中,那么计算从p 到任何一个的距离都是未定义的行为。在这种情况下,如何使用它来确定p 是否是[a;b) 数组的成员?
  • @Frodyne 如果可以保证ab 属于同一个数组并且结果与之一致,那么它确实属于范围。如果你不能保证[a, b)是合法的范围,你不能保证结果的正确性,在这种情况下某些实现可能会产生异常
  • 我对OP问题的解释是:T arr[27]; a=begin(arr); b=end(arr);然后问题是;对于一些随机指针T* p;p 指向arr 的成员(即在[a;b) 范围内)。在这种解释下,distance(a,b) 当然是有效的,但只有当p 确实指向arr 时才会定义distance(a,p)。我错了,误解了什么,还是您以不同的方式阅读 OP 的问题?
  • @Frodyne 从技术上讲,如果 (a,p) 为正且 (p,b) 为正距离且它们的和等于 (a,b),则 p 应该是数组的一部分。如果我们假设它不是数组的一部分,则意味着有一些指针 q 与 p 具有相同的距离,即它是相同的指针:q = a + std::distance(a,p)。根据定义q 指向同一个对象p,这与假设相矛盾。注意,C++ 保证地址的唯一性。第二个限制确保 p 没有越界。现在,如果 a 和 b 不是同一范围的边界,我们就没有这个保证。
  • @Frodyne 唯一的极端情况是 p 是从联合的替代成员创建的。内存位置或地址可以共享,但需要正式清洗该指针。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多