C++没有告诉你动态数组的大小.但为什么?

jar*_*auh 61 c++ dynamic-arrays

我知道在C++中无法获得动态创建的数组的大小,例如:

int* a;
a = new int[n];
Run Code Online (Sandbox Code Playgroud)

我想知道的是:为什么?人们是否在C++规范中忘记了这一点,还是有技术原因?

信息不是存储在某个地方吗?毕竟,命令

delete[] a;
Run Code Online (Sandbox Code Playgroud)

似乎知道它必须释放多少内存,所以在我看来,delete[]有一些方法可以知道它的大小a.

Mar*_*ica 38

这是"不为你不需要付出代价"的基本规则的后续行动.在您的示例delete[] a; 不需要知道数组的大小,因为int没有析构函数.如果你写了:

std::string* a;
a = new std::string[n];
...
delete [] a;
Run Code Online (Sandbox Code Playgroud)

然后delete必须调用析构函数(并且需要知道要调用多少) - 在这种情况下new必须保存该计数.然而,由于它并不需要被保存在所有场合,Bjarne的决定不给访问它.

(事后看来,我认为这是一个错误...)

即使int当然,事情有了解的分配内存的大小,但是:

  • 为了对齐和方便的原因,许多分配器将大小四舍五入到一些方便的倍数(比如64字节).分配器知道一个块长64个字节 - 但它不知道是因为n是1还是16.

  • C++运行时库可能无法访问已分配块的大小.例如,new并且delete正在使用mallocfree引擎盖下,那么C++库无法知道返回的块的大小malloc.(一般是当然,new而且malloc是同一个库的两个组成部分-但并不总是)

  • @bolov不一定.分配块的大小可能是分配器已知的,但可能不是您要求的大小; 可以分配更大的块,例如避免碎片或某些平台上的对齐问题. (18认同)
  • 即使使用int,也不必知道大小吗?在任何结构中标记内存空闲以保持内存分配/空闲的证据? (4认同)
  • 如果这是一个错误,至少它是一个向前兼容的错误.如果委员会决定要纠正它,他们可以这样做而不会破坏任何东西.反之则不然. (4认同)
  • 当数组元素类型不是简单的可破坏时,C++ 14需要array-delete来调用大小的释放函数.[演示](http://melpon.org/wandbox/permlink/EihMitkUJsyBDt7O) (4认同)

mol*_*ilo 13

一个基本原因是指​​向动态分配的数组的第一个元素的T指针和指向任何其他数组的指针之间没有区别T.

考虑一个虚构函数,它返回指针指向的元素数.
我们称之为"大小".

听起来真的很好,对吧?

如果不是因为所有指针都是相同的:

char* p = new char[10];
size_t ps = size(p+1);  // What?

char a[10] = {0};
size_t as = size(a);     // Hmm...
size_t bs = size(a + 1); // Wut?

char i = 0;
size_t is = size(&i);  // OK?
Run Code Online (Sandbox Code Playgroud)

你可以说第一个应该是9,第二个10,第三个9,也可以是最后一个1,但要完成这个,你需要在每个对象上添加一个"大小标签" .
A char将需要64位计算机上的128位存储(因为对齐).这比必要的多16倍.
(上面,十个字符的数组a至少需要168个字节.)

这可能很方便,但也是不可接受的昂贵.

当然,如果参数确实是默认情况下动态分配的第一个元素的指针,那么你当然可以想象一个只有良好定义的版本operator new,但这并不像人们想象的那么有用.

  • 我理解这一点.我想知道的是:当数组被删除时,突然编译器似乎记得有关数组大小的东西.我从上面的评论中学到的是,编译器只记得保留了多少内存,这只给出了数组大小的下限. (3认同)
  • 更简单的解决方案是所有这些用途都是未定义的行为.`size()`可以定义为仅适用于`new []`数组. (3认同)
  • @jarauh尝试删除`a [1]`.你期望发生什么?究竟发生了什么?:)另外,为什么你认为*编译器*知道数组有多大?在我不看的时候,C++编译器是否解决了暂停问题?:d (2认同)
  • 您可能还想调出子数组。我可能会使用 new 来生成 10 个 char 的数组,然后将其用作两个单独的 5 个 char 的数组。编译器无法知道我为数组的不同部分赋予了不同的语义,因此“size()”函数会给出错误的值。 (2认同)
  • @Luaan删除`&a [1]`如果后备内存分配器在其分配范围内的任何地方使用指针接受free,则可以使用普通对象.C++不要求这样做,并且要求你从你从`new`获得的地址中删除 (2认同)

小智 2

您经常会发现内存管理器只会分配一定倍数的空间,例如 64 字节。

因此,您可能会请求 new int[4],即 16 个字节,但内存管理器将为您的请求分配 64 个字节。要释放该内存,它不需要知道您请求了多少内存,只需知道它已为您分配了一个 64 字节的块。

下一个问题可能是,它不能存储请求的大小吗?这是一项额外的开销,并不是每个人都愿意为此付出代价。例如,Arduino Uno 只有 2k RAM,在这种情况下,每个分配的 4 个字节突然变得很重要。

如果您需要该功能,那么您可以使用 std::vector (或等效语言),或者使用更高级的语言。C/C++ 的设计目的是让您能够以尽可能少的开销进行工作,这就是一个例子。