获得动态分配的数组大小

时间:2013-09-02 18:02:22

标签: c++ memory-management new-operator

In" C ++编程语言"书Stroustrup说:

  

"要解除分配分配的空间,删除删除[] 必须能够确定对象的大小分配。这意味着使用 new 的标准实现分配的对象将占用比静态对象稍多的空间。通常,使用一个单词来保存对象的大小。

这意味着new分配的每个对象的大小都位于堆中的某个位置。该位置是否已知,是否可以访问它?

3 个答案:

答案 0 :(得分:6)

实际上,内存分配器的典型实现也存储了一些其他信息。

没有标准的方法来访问这些信息,实际上标准中没有任何内容说明存储了什么信息(大小以字节为单位,元素数量及其大小,指向最后一个元素的指针等)。

编辑: 如果你有对象的基地址和正确的类型,我怀疑分配的大小可以相对容易地找到(不一定是#34;完全没有成本")。但是,有几个问题:

  1. 假设您有原始指针。
  2. 假设内存与运行时库的分配代码完全分配。
  3. 假设分配器没有"#34;以某种方式分配地址。
  4. 为了说明这可能出错,请说我们这样做:

    size_t get_len_array(int *mem)
    {
       return allcoated_length(mem);
    }
    
    ... 
    void func()
    {
        int *p = new int[100];
        cout << get_len_array(p); 
        delete [] p;
    }
    
    void func2()
    {
        int buf[100];
        cout << get_len_array(buf); // Ouch!
    }
    

答案 1 :(得分:1)

  

这意味着new分配的每个对象的大小都位于堆中的某个位置。该位置是否已知,是否可以访问它?

并非真的,所有案例都不需要。为简化推理,有两个级别可能需要大小。在语言级别,编译器需要知道要销毁什么。在分配器级别,分配器需要知道如何仅在给定指针的情况下释放内存。

在语言级别,只有数组版本new[]delete[]需要处理任何大小。当您使用new进行分配时,您将获得一个包含对象类型的指针,并且该类型具有给定的大小。

要销毁对象,不需要大小。当您delete时,指针指向正确的类型,或者指针的静态类型是基础,析构函数是虚拟的。所有其他情况都是未定义的行为,因此可以忽略(任何事情都可能发生)。如果它是正确的类型,那么大小是已知的。如果它是具有虚拟析构函数的基础,则动态调度将找到最终的覆盖,并且此时类型是已知的。

可能有不同的策略来管理它,Itanium C ++ ABI中使用的策略(多个平台中的多个编译器使用,但不是Visual Studio)例如每种类型最多可生成3个不同的析构函数,其中一个是负责释放内存的版本,所以虽然delete ptr是根据调用适当的析构函数然后释放内存来定义的,但在这个特定的ABI delete ptr中调用一个特殊的析构函数来销毁和释放存储器中。

使用new[]时,无论动态数组中的元素数量是多少,指针的类型都是相同的,因此不能使用该类型来检索该信息。一个常见的实现是分配一个额外的整数值并在那里存储大小,然后是真实对象,然后返回指向第一个对象的指针。然后delete[]将接收到的指针移回一个整数,读取元素数量,为所有元素调用析构函数然后释放内存(分配器检索的指针,而不是指定给程序的指针)。这实际上只有在类型具有非平凡的析构函数时才需要,如果类型具有普通的析构函数,则实现不需要存储大小,您可以避免存储该数字。

在语言级别之外,真实内存分配器(想象malloc)需要知道分配了多少内存,以便可以释放相同的内存。在某些情况下,可以通过以与new[]存储数组大小相同的方式将元数据附加到内存缓冲区来完成,通过获取更大的块,在那里存储元数据并在其之外返回指针。然后,解除分配器将撤消转换以获取元数据。

另一方面,这并非总是需要。小规模分配器的常见实现是分配内存页面以形成池,然后从中获得小分配。为了提高效率,分配器只考虑几种不同的大小,并且不完全适合其中一种大小的分配将被提升到下一个大小。例如,如果您请求65字节,分配器实际上可能会给您128个字节(假设64和128字节的池)。因此,给定分配器管理的较大块之一,从其分配的所有指针具有相同的大小。然后,分配器可以找到分配指针的块并从中推断出大小。

当然,这是C ++程序无法以标准可移植方式访问的所有实现细节,并且确切的实现不仅可以基于程序而且还可以根据执行环境而有所不同。如果您有兴趣知道如何在您的环境中保存信息,您可能能够找到这些信息,但在尝试将其用于除学习目的之外的其他任何事情之前,我会三思而行。

答案 2 :(得分:0)

您不是直接删除对象,而是向delete运算符发送指针。 参考C ++
您可以使用以下删除 它带有指向最初用new分配的内存块的指针:

int * ps = new int; // allocate memory with new
           . . .  // use the memory
delete ps;          // free memory with delete when done

这将删除ps指向的内存;它不会删除指针ps本身。 例如,您可以重用ps来指向另一个新分配