【问题标题】:C++ Detect dangling pointers when they are going out of bound without knowing the sizeC++ 在不知道大小的情况下检测悬空指针越界
【发布时间】:2012-08-01 11:46:05
【问题描述】:

考虑一段代码

int main(int argc,char* argv[]){
        int *arrPtr = new int[4];
        for (int i=0;i<4;i++)
                arrPtr[i]=i*2;


        for (int i=0;i<10;i++){
                arrPtr++;
                cout<<"arrPtr ["<<i<<"]\t"<<*arrPtr<<endl;
        }
        cout<<endl;
        return 0; }

当我编译这个程序时

g++ main.cpp -o main && ./main

它给了我

arrPtr [0] 2 arrPtr [1] 4 arrPtr [2] 6 arrPtr [3] 0 arrPtr [4] 135145 arrPtr [5] 0 arrPtr [6] 0 arrPtr [7] 0 arrPtr [8] 0 arrPtr [9] 0

我希望能够在 arrPtr 未指向“有效”元素时检测(抛出或检查)。 在上面的代码中,最后一个有效元素是 arrPtr[3]。

请不要回答“我也是”!

【问题讨论】:

  • 你不能。只需以一种或另一种形式传递尺寸。例如。使用std::vector 而不是普通数组(并通过v.at(i) 而不是v[i] 访问它)。
  • @atzz oops 在发布后看到了您的评论..
  • 请注意,如果您有一个值数组(不是动态分配的指针),则始终可以通过执行sizeof(array)/sizeof(type_of_array) 来获取大小。也就是说,您显然需要同质类型来计算它。

标签: c++ arrays pointers null


【解决方案1】:

使用普通的 C 数组是不可能的。您可以使用 std::vector,它具有用于通过边界检查访问元素的 API。

【讨论】:

    【解决方案2】:

    使用vector

    int main()
    {
        std::vector<int> a(4);
    
        for (size_t i = 0; i < 4; ++i)
            a[i] = i * 2;
        for (size_t i = 0; i < 10; ++i)
            std::cout << i << ": " << a.at(i) << std::endl;
    }
    

    结果:

    0: 0
    1: 2
    2: 4
    3: 6
    terminate called after throwing an instance of 'std::out_of_range'
      what():  vector::_M_range_check
    Aborted
    

    【讨论】:

    • 不,就是这样,我不打算使用向量,因为我正在研究通用框架。用户可以使用 `MyClass *c=new MyClass[]'
    • @mkhan3189:那你就卡住了。除了std::vector,边界检查不能移植。
    • @mkhan3189 使用向量有什么不通用的?这是要走的路,期间。
    • @mkhan3189 您是否考虑过在自己的函数中使用迭代器(与标准库函数相同)?这适用于向量、数组或任何容器。
    • @mkhan3189 你使用的任何技术都是错误的,对不起。这在 C++ 中根本是不可能的,而且无论你尝试什么,都会有它不起作用的情况。它不能在一般情况下工作,因为相同的指针值可以同时指向不同长度的不同数组(如果两个不同长度的数组出现在一个联合中)。而且它在实践中很可能行不通,因为在数组结束后通常会有可寻址的内存。
    【解决方案3】:

    其他人已经展示了这个具体问题的解决方案:使用 std::vector 而不是原始指针(无论如何都要这样做!)并通过 .at() 访问元素。

    但是,一般而言,您应该确保代码中不会出现这种情况。越界错误不是您应该在运行时处理的情况,它们是代码中的错误,需要事先删除。

    对此有几种策略——例如,不要使用索引访问。大多数这样的实例都可以被迭代器访问所取代,虽然这不是自动安全的,但更难出错。

    更好的是,尽可能避免循环,并改用 C++ 标准库中的高阶算法。

    作为一个无循环代码的示例:

    std::vector<int> arr;
    std::generate_n(std::back_inserter(arr), 4,
        [] {
            static int current = 0;
            return current++ * 2;
        });
    std::copy(begin(arr), end(arr), std::ostream_iterator<int>(std::cout, "\n"));
    

    这种风格需要一些时间来适应,但它大大减少了在代码中出错的机会,并且使某些错误类不可能成为专有的。

    【讨论】:

      【解决方案4】:

      这个话题已被广泛讨论。通常,在C语言中,如果事先不知道数组的一些信息,就无法检测指针是否指向数组中的某个位置。

      正如@Charlie Martin 在他对this post 的回答中提到的那样,这是因为边界检查将花费额外的操作。由于 C 常用于制作对速度敏感的程序(如硬件驱动程序),因此边界检查不是用该语言实现的,而是由需要它的人自己实现的。

      因此,如果您想进行边界检查,您必须执行以下操作:

      int *start = new int[4];
      int size=4;
      int *arrPtr;
      ...
      for (int i=0,arrPtr=start;i<10;i++){
          arrPtr++;
          if(arrPtr>=start && arrPtr <start+size) // the boundary checking by yourself
             cout<<"arrPtr ["<<i<<"]\t"<<*arrPtr<<endl;
      }
      

      通过这种方式,语言可以让您决定是否值得和有必要使用额外的操作来进行边界检查。

      当然,一旦您决定需要在程序中进行边界检查,就已经实现了更高级别的数据类型,例如 vector

      【讨论】:

        猜你喜欢
        • 2022-01-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-02-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-24
        相关资源
        最近更新 更多