【问题标题】:checking if pointer points within an array检查指针是否指向数组
【发布时间】:2011-06-07 04:14:45
【问题描述】:

我能否检查给定的指针是否指向数组中的对象,由其边界指定?

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

如果p 指向数组边界之外,指针比较会调用未定义的行为吗?在这种情况下,我该如何解决问题?它适用于 void 指针吗?还是无法解决?

【问题讨论】:

  • 您的比较有效,但并不能真正保证“结束”与数组有关 - 我认为您最好使用大小(请参阅下面的答案)。
  • @jweyrich?你为什么这么说?假设 OP 只使用定义明确的指针转换,程序中的每个指针都指向对齐良好的数据(或者它们是 null)。根据 C++ 标准,未对齐的数据基本上不存在。 p 保证对齐,除非调用站点存在未定义的行为。
  • @jalf:我撤回了我的评论。谢谢你的课。
  • @BeeBand:下面的任何人都没有考虑过,但这是正确的。

标签: c++ arrays pointers comparison


【解决方案1】:

这样做的唯一正确方法是这样的方法。

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

很明显,如果T == void,这将不起作用。我不确定两个 void* 在技术上是否定义了一个范围。当然,如果你有Derived[n],那么说(Base*)Derived, (Base*)(Derived + n) 定义了一个有效范围是不正确的,所以我看不出用除了指向实际数组元素类型的指针之外的任何东西来定义一个范围是有效的。

下面的方法失败,因为如果两个操作数不指向同一对象的成员或同一数组的元素,则未指定 &lt; 返回的内容。 (5.9 [expr.rel] / 2)

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

下面的方法失败,因为如果两个操作数不指向同一对象的成员或同一数组的元素,std::less&lt;T*&gt;::operator() 返回的内容也未指定。

如果内置的&lt; 没有,std::less 必须专门用于任何指针类型以产生总顺序,但这仅适用于为set 或提供键等用途map。不能保证总顺序不会将不同的数组或对象交错在一起。

例如,在分段内存架构上,对象偏移量可用于&lt;,并作为std::less&lt;T*&gt; 的最重要区别,段索引用于打破联系。在这样的系统中,一个数组的元素可以在第二个不同数组的边界之间进行排序。

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

【讨论】:

  • 你能用 == 来比较这样的指针吗,例如在分段架构上? stackoverflow.com/q/4909766/511601
  • @FredNurk: == 必须能够比较来自不同完整对象的指针,如果这涉及比较段标识符,那么这就是实现必须做的事情。 (如果存在诸如“当前段”之类的东西并且可以更改,那么段标识符必须是作为指针存储的内容的一部分;如果您在不同的上下文。)
【解决方案2】:

你能不能用std::distance 做这个,即你的问题实际上归结为:

return distance(begin, p) >= 0 && distance(begin, p) < distance(begin, end);

鉴于这个随机访问迭代器(指针)被传入,它应该归结为一些指针算术而不是指针比较? (我假设 end 真的是 end 而不是数组中的最后一项,如果最后一项则将小于更改为 &lt;=)。

我可能离题...

【讨论】:

    【解决方案3】:

    直接来自MSDN documentation

    不能比较两个不同类型的指针,除非:

    • 一种类型是从另一种类型派生的类类型。
    • 至少有一个指针被显式转换(强制转换)为 void * 类型。 (另一个指针被隐式转换为 void * 类型以进行转换。)

    所以void* 可以与其他任何东西(包括另一个void*)进行比较。但是比较会产生有意义的结果吗?

    如果两个指针指向 相同的数组或元素一 超出数组的末尾, 指向具有较高值的​​对象的指针 下标比较高。比较 的指针保证仅有效 当指针指向对象时 相同的阵列或位置一 超过数组的末尾。

    好像没有。如果您不知道您正在比较数组中的项目(或只是过去),则不能保证比较是有意义的。

    但是,有一个解决方案:STL 提供了std::less&lt;&gt;std::greater&lt;&gt;,它们适用于任何指针类型,并且在所有情况下都会产生有效的结果:

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

    更新:

    this question 的答案给出了相同的建议 (std::less) 并引用了标准。

    【讨论】:

    • 我不会引用 MSDN 作为权威的 C++ 源代码,因为它通常不准确或特定于 MS。
    • @Maxim Yegorushkin:我同意这一点。但是他们已经做了很多工作来指出特定于 MS 的问题并大大改善了这个问题。另外,这只是证实了我已经“知道”的事情。最后,IMO MSDN 比标准更容易搜索。
    • 您的引用谈到了不同类型的指针,而 OP 对比较相同类型的指针感兴趣。
    • @Peter:OP 询问“它是否适用于 void 指针?”。这就是第一部分所要解决的问题;我认为这很清楚,因为我明确指出了这一点。
    • @Jon,你是对的,我的遗漏,问题确实存在,尽管作为文本中的次要问题。显示的代码示例定义了一个带有T* 参数的函数,因此它们应该属于同一类型。如果您允许我提出使您的答案更清晰的建议,那么引用您正在回答的特定(子)问题可能会有所改进。
    【解决方案4】:

    虽然比较只对数组内的指针和“结束后的一个”有效,但使用以指针为键的集合或映射是有效的,它使用std::less&lt;T*&gt;

    早在 1996 年,comp.std.c++ 就对这种方式进行了一次大讨论

    【讨论】:

    • 是因为 std::less 以某种方式神奇地避免了比较吗?
    • @Maxim: std::less 需要实现获得一致结果所需的任何魔法。它可能表示地址0A00:0001 小于0100:0001,但可能不表示它们是等价的。 operator&lt; 只允许比较偏移量并表明这些地址是等价的。
    • 你能给我推荐任何标准的 C++ 标准库实现吗?
    • 但是,当您将 std::less 应用于来自不同对象的指针时,并不要求一个范围中的一个不在第二个范围的边界之间排序,这使得您可以进行比较他们没有实际意义。
    • @Maxim,所有标准库都这样做。问题是找到具有标准比较运算符而不是基于总顺序的实现。我不确定是否还有许多针对 x86 分段模式的编译器。
    【解决方案5】:

    指针类型的比较不一定会产生总顺序。但是,std::less/std::greater_equal 可以。所以...

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

    会起作用的。

    【讨论】:

    • A 总订单,不一定是单独的范围不“重叠”的订单。
    • 如果范围可以重叠,则不是总订单。
    • @fizzer:可以。想象一下,与数组 A 的索引相比,数组 B 出现在 0.5、1.5、2.5 的位置。
    • 如果您想要 100% 符合您期望的代码,请不要使用此代码。
    【解决方案6】:

    C++ 标准未指定当您比较指向不在同一数组中的对象的指针时会发生什么,因此未定义行为。但是,C++ 标准并不是您的平台必须遵守的唯一标准。诸如 POSIX 之类的其他标准将 C++ 标准留下的东西指定为未定义的行为。 在 Linux 和 Win32/64 等具有虚拟地址空间的平台上,您可以比较任何指针而不会导致任何未定义的行为。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-20
      • 1970-01-01
      相关资源
      最近更新 更多