1

我读了C中是否允许负数组索引?并发现有趣的是负值可以用于数组的索引。我用 c++11 再次尝试了它unique_ptr,它也可以在那里工作!当然删除器必须替换为可以删除原始数组的东西。这是它的样子:

#include <iostream>
#include <memory>

int main()
{
    const int min = -23; // the smaller valid index
    const int max = -21; // the highest valid index
    const auto deleter = [min](char* p)
    {
        delete [](p+min);
    };
    std::unique_ptr<char[],decltype(deleter)> up(new char[max-min+1] - min, deleter);

    // this works as expected
    up[-23] = 'h'; up[-22] = 'i'; up[-21] = 0;
    std::cout << (up.get()-23) << '\n'; // outputs:hi
}

我想知道是否存在内存泄漏的可能性非常小。在堆 ( ) 上创建的内存地址在new char[max-min+1]向其添加 23 时可能会溢出并成为空指针。减去 23 仍会产生数组的原始地址,但unique_ptr可能会将其识别为空指针。可能不会删除它,unique_ptr因为它为空。

那么,之前的代码是否有可能泄漏内存,或者智能指针的行为方式是否安全?

注意:我实际上不会在实际代码中使用它;我只是对它的行为方式感兴趣。

4

2 回答 2

4

编辑: icepack提出了一个有趣的观点,即在指针算术中只允许两个有效的指针值:

§5.7 [expr.add] p5

如果指针操作数和结果都指向同一个数组对象的元素,或者超过数组对象的最后一个元素,则计算不应产生溢出;否则,行为是 undefined

因此,new char[N] - min您的代码已经调用了 UB。


现在,在大多数实现中,这不会导致问题。但是,的析构函数std::unique_ptr将(从这里开始预编辑答案):

§20.7.1.2.2 [unique.ptr.single.dtor] p2

效果:如果get() == nullptr没有效果。否则get_deleter()(get())

所以是的,如果它确实映射到代表空指针值的任何值(最有可能0,但不一定),你就有可能在这里泄漏内存。是的,我知道这是针对单个对象的,但数组 1 的行为完全相同:

§20.7.1.3 [unique.ptr.runtime] p2

下面仅针对行为不同于主模板的成员函数提供描述。

并且没有关于析构函数的描述。

于 2012-10-29T06:08:31.280 回答
3

new char[max-min+1]不在堆栈上分配内存,而是在堆上分配内存——这就是标准的operator new行为方式。该表达式max-min+1由编译器评估并产生3,因此最终该表达式等于在堆上分配 3 个字节。这里没问题。

但是,减去min会导致指针超出所分配内存的开头 23 个字节,new并且由于在 new 中您只分配了 3 个字节,这肯定会指向一个不属于您的位置 --> 任何以下内容都将导致未定义的行为.

于 2012-10-29T06:16:32.553 回答