C ++不会告诉你dynamic数组的大小。 但为什么?
我知道在C ++中没有办法获得dynamic创build的数组的大小,例如:
int* a; a = new int[n];
我想知道的是:为什么? 在C ++的规范中,人们是否忘记了这一点,还是有技术上的原因呢?
信息是否存储在某个地方? 毕竟,这个命令
delete[] a;
似乎知道它要释放多less内存,所以在我看来, delete[]
有一些方法知道a
。
你会经常发现内存pipe理器只会分配一定数量的空间,例如64个字节。
所以,你可能会要求新的int [4],即16个字节,但是内存pipe理器会为你的请求分配64个字节。 为了释放这个内存,它不需要知道你要求多less内存,只是它已经分配给你一个64字节的数据块。
下一个问题可能是,难道它不能存储所需的大小? 这是一个额外的开销,并非每个人都愿意付钱。 例如Arduino Uno只有2k的RAM,在这种情况下,每个分配4个字节突然变得很重要。
如果你需要这个function,那么你有std :: vector(或等价的),或者你有更高级的语言。 C / C ++被devise为使您能够尽可能less地使用开销,这只是一个例子。
这是从“不要付你不需要的东西”这个根本原则出发的。 在你的例子中delete[] a;
不需要知道数组的大小,因为int没有析构函数。 如果你写了:
std::string* a; a = new std::string[n]; ... delete [] a;
然后delete
必须调用析构函数(并需要知道有多less个调用) – 在这种情况下, new
必须保存这个计数。 然而,由于在任何情况下都不需要保存,所以Bjarne决定不让它访问。
(事后看来,我认为这是一个错误…)
即使是int
,当然也要知道分配内存的大小,但是:
-
为了alignment和便利的原因,许多分配器将大小凑成一些方便的倍数(比如64字节)。 分配器知道一个块的长度是64字节 – 但是不知道是否因为
n
是1 …或者16。 -
C ++运行时库可能无法访问分配的块的大小。 例如,如果
new
和delete
正在使用malloc
和free
下,那么C ++库就无法知道malloc
返回的块的大小。 (通常当然,new
和malloc
都是同一个库的一部分,但并不总是如此)。
一个根本的原因是指向T
的dynamic分配数组的第一个元素的指针与指向任何其他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?
你可能会认为第一个应该是9
,第二个10
,第三个9
和最后1
,但是为了完成这个,你需要在每个对象上添加一个“大小标记”。
char
在64位机器上需要128位的存储空间(因为alignment)。 这是必要的十六倍。
(上面,十个字符的数组a
至less需要168个字节。)
这可能是方便的,但也是不可接受的昂贵。
你当然可以设想一个只有定义好的版本,如果这个参数真的是一个指向默认operator new
的dynamic分配的第一个元素的指针,但是这不像人们想象的那么有用。
你是对的,系统的某些部分将不得不知道有关的大小。 但是获取这些信息可能不被内存pipe理系统的API所覆盖(想想malloc
/ free
),并且你所要求的确切的大小可能不知道,因为它可能已经被四舍五入了。
有一个奇怪的情况是重载了我以下面的formsfind的operator delete
:
void operator delete[](void *p, size_t size);
参数大小似乎默认为void * p指向的内存块大小(以字节为单位)。 如果这是真的,那么至less希望它有一个由operator new
的调用传递的值是合理的,因此,只需要除以sizeof(type)来传递存储在数组中的元素的数量。
至于你的问题的“为什么”这一部分, 马丁的“不要付你不需要的东西”的规定似乎是最合乎逻辑的。
没有办法知道你将如何使用这个数组。 分配大小不一定匹配元素编号,因此您不能只使用分配大小(即使可用)。
这是C ++中其他语言的一个深层次的缺陷。 你用std :: vector实现你想要的function,但仍保留对数组的原始访问。 保留原始访问对于任何需要做某些工作的代码来说都是至关重要的。
很多时候,您将对数组的子集执行操作,并且当您在语言中内置了额外的书籍logging时,您必须重新分配子数组并复制数据,以便使用期望托pipe数组的API来操作它们。
只要考虑对数据元素进行sorting即可。 如果你有托pipe数组,那么你不能使用recursion而不复制数据来创build新的子数组来recursion地传递。
另一个例子是recursion操作以2×2“蝴蝶”开始的数据并返回整个数组的FFT。
要修复托pipe数组,现在需要“别的东西”来修补这个缺陷,而“别的东西”被称为“迭代器”。 (你现在拥有托pipe数组,但是几乎从不将它们传递给任何函数,因为在90%的时间内需要迭代器)。
分配有new[]
的数组的大小不会被可见地存储在任何地方,因此您无法访问它。 new[]
运算符不返回数组,只是指向数组的第一个元素的指针。 如果你想知道一个dynamic数组的大小,你必须手动存储它,或者使用库中的类,如std::vector