动态分配数组的大小如何确定?

27

为什么无法获取以此方式分配的缓冲区的长度。

AType * pArr = new AType[nVariable];

当同一数组被释放时

delete [] pArr;

运行时需要知道需要释放的内存大小。是否有方法在删除数组之前访问其长度?如果没有,为什么没有提供可以获取长度的API?


最好的方法是使用 std::vector<AType> pArr(nVariable);。这将在内部使用动态分配,您可以随时方便地使用 .size() - iammilind
8个回答

9
有没有办法在删除数组之前访问其长度呢?
不可以。标准不要求实现记住并提供通过`new`请求的元素数量的具体信息。
实现可能只是在分配的内存块末尾插入特定位模式,而不是记住元素数量,并在释放内存时简单地查找该模式。
简而言之,这完全是一种实现细节。
顺便说一下,有两个实际解决此问题的选项:
1.您可以简单地使用std :: vector ,它提供了像size()这样的成员函数
2.您也可以自己进行簿记记录。
new至少分配与您请求的内存一样多的内存。 您已经知道请求了多少内存,因此可以轻松计算长度。 您可以使用sizeof找到每个项目的大小。
Total memory requested / Memory required for 1 item = No of Items

4
我没有给你点踩,但我认为问题在于他问为什么标准规定不提供那个东西,而你却回答说它是因为标准如此规定。他想知道的是背后的理由,而不是一个具体问题的解决方案。 - akappa
@akappa:我的回答说明了理由,实现可以有更有效/更好的方式来跟踪分配的内存,因此标准并不强制要求以特定的方式进行。事实上,答案甚至提到了其中一种方式。既然你说这不是理由,请告诉我你认为这不是理由的原因/如何看待这个问题? - Alok Save
1
“插入模式”并不会阻止您报告块的长度:它只是一个非常量查询。我认为在某些情况下,C++ RTS 可能无论如何都不知道该信息,因为(比如说)它由操作系统保存,并且他不愿意向您透露(但它可以公开一个函数,让您删除内存块并将函数应用于正在删除的内存,例如调用 dtors)。简而言之,这是一种情况,其中 C++ RTS 依赖于更低级别的 RTS,因此标准对实现施加了最低限制以降低成本。 - akappa

6

运行时确实知道分配了多少内存。 但是这些细节取决于编译器,因此您没有任何跨平台的方法可以处理它。

如果您想要相同的功能并能够跟踪大小,则可以使用std::vector如下:

std::vector< AType > pArr( nVariable );

这样做还有一个额外的优点,它也使用了 RAII。


我同意实现是编译器特定的。但是,标准为什么不要求C++供应商公开以下API之类的API呢? size_t array_sizeof(void const * pHead); - Ram
@Ram:我不知道是什么阻止了标准要求这样做,因为许多运行时都允许它。可能有一些实现分配的方式无法做到这一点,这就是不强制暴露它的原因。然而,我想不出任何一种分配模式不能这样做... - Goz
@Ram:个人而言,我总是使用向量而不是动态分配,以便获得RAII、复制语义和大小跟踪的好处。 - Goz

3
< p > delete 操作符不需要知道大小就可以释放已分配的内存,就像 free 系统调用一样。这是因为这个问题留给操作系统而不是编译器的运行时系统。


1
并不是这样,删除操作和释放库函数需要知道这一点。我认为许多实现不会将堆分配的内存返回给操作系统,而是重复使用它,即使它们确实返回内存,也没有1:1映射关系,即释放/删除调用和返回内存的系统调用之间通常不存在(这是因为操作系统通常不允许您以小块返回内存,而是以页面大小的倍数)。 - Jan Wrobel
1
至少对于非POD数据类型,运行时必须知道它,因为它必须调用析构函数。对于POD数据类型来说,可以说C++运行时系统可能或可能不知道分配了多少空间,因为裸内存处理不在标准范围内。 - akappa

3

运行时必须释放与分配的相同数量,并以某种方式(通常非常间接地)跟踪此内容。但是,没有可靠的方法可以从分配的数量转换为元素的数量:分配的数量不能少于每个元素的大小乘以元素数,但通常会更多。例如,对齐考虑因素意味着 new char[5]new char[8] 通常会分配相同数量的内存,并且有各种分配策略可以导致分配比严格必要的内存更多。


2
不,实际上不行。至少没有一种跨平台的、定义明确的方法。
大多数实现在实际数组之前存储动态分配数组的大小。

3
真的。我看到的那些没有。 - James Kanze
@JamesKanze 微软视窗开发环境肯定支持。 - Luchian Grigore
我刚刚试过使用VC10。如果我执行“new char [10]”,则返回指针之前的“size_t”值为“917978”(在g ++上,它包含“17”,这实际上可能与分配的内存量有关,因为对齐考虑将需要将其四舍五入到下一个8的倍数)。 - James Kanze

1
我使用这个“不太规范”的方法,仅用于调试目的:
T *p = new T[count];
size_t size = (char*)&(p[count]) - (char*)p;

这提供了实际数据的大小,但不包括编译器可能分配的任何额外空间。
对于已经对齐的类型T,它等于:
size_t size = sizeof(T) * count;

当然,如果您不知道数组中项的数量,则此方法无法运作。

1

在C++中,没有一种便携式的方法可以从原始指针中获取动态分配数组的大小。

在MSVC和WIN32下,您可以使用_msize(void*)函数获取分配块的大小。

有关详细信息,请参见https://msdn.microsoft.com/en-us/library/z2s077bc.aspx


0

为什么不添加一些额外的信息呢:

template <typename T> class AType
{
public:

    AType(size_t s) : data(0)
    {
        a_size = s;
        data = new T[s];
    }
    ~AType() {
        if (data != nullptr)
            delete [] data;
    }

    size_t getSize() const
    {
        return a_size * sizeof(T);
    }

private:
    size_t a_size;
    T* data;
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接