cout << new int[0] << endl;
输出:
0x876c0b8
所以看起来像它。标准对此有何说法? “分配”空的内存块始终合法吗?
#1 楼
从5.3.4 / 7起当direct-new-declarator中的表达式的值为零时,将调用分配函数以分配不包含任何元素的数组。
从3.7.3.1/2
取消解引用作为零大小请求返回的指针的效果是不确定的。
也
即使请求的空间大小(由new决定)为零,请求也可能失败。
这意味着您可以执行此操作,但是您不能合法地(在所有平台上以明确定义的方式)取消引用所获得的内存-您只能将其传递给array delete-并且应该删除它。
这是从3.7.3.1/2
[32.]的句子所附的有趣的脚注(即,不是标准的规范性部分,但出于说明目的而包括在内)。目的是通过调用malloc()或calloc()使操作符new()可以实现,因此规则基本相同。 C ++与C的不同之处在于C要求零请求以返回非null指针。]
评论
如果不删除,则会发生内存泄漏。是预期的吗?至少我没想到。
– EralpB
2014年10月10日13:46
@EralpB:相反,即使您的请求为零,此分配也会在堆上发生,其中一个请求意味着几个簿记操作,例如在分配器给定的区域之前和之后分配和初始化堆防护,插入到空闲列表或其他复杂的恐怖结构。释放它意味着进行向后簿记。
–v.oddou
14年6月13日在12:18
@EralpB是的,我想每当您不平衡new []和delete []的大小时,您都会遇到内存泄漏。特别是,当您调用new [i]时,您需要的内存要比您要分配的要多一些,以便存储数组的大小(以后在分配时由delete []使用)
– pqnet
17-10-20在15:51
#2 楼
是的,分配这样的零大小数组是合法的。但是您还必须删除它。评论
您对此有引文吗?我们都知道int ar [0];非法为什么可以新?
–莫蒂
09年7月6日在13:47
有趣的是,C ++在禁止零尺寸对象方面没有那么强大。想想空基类优化:在这里,空基类子对象的大小也可能为零。相比之下,C标准付出了巨大的努力,以确保永远不会创建零尺寸的对象:例如,在定义malloc(0)时,它的作用是使用一些非零参数运行malloc。而在结构{...; T n []; };当没有为数组(FAM)分配空间时,它表示其行为就像“ n”具有一个元素。 (在两种情况下,以任何方式使用对象都是UB,例如x.n [0])
– Johannes Schaub-小人
09年7月6日在16:25
我认为sizeof(type)永远不会返回零。参见例如:stackoverflow.com/questions/2632021/can-sizeof-return-0-zero
– pqnet
17-10-20在15:54
甚至所谓的“空基类优化”也仅是相关的,因为坚持要求所有对象(与对象中的所有字节相对)必须具有唯一的地址。如果需要真正关心具有唯一地址的对象的代码以确保它们的大小为非零,则可以简化C ++。
–超级猫
18年6月27日在21:26
#3 楼
标准对此有何表述? “分配”空的内存块总是合法吗?
每个对象都有唯一的标识,即唯一的地址,这意味着长度为非零(实际内存量)如果您要求零字节,则将以静默方式增加)。
如果您分配了多个这些对象之一,则会发现它们具有不同的地址。
评论
“每个对象都有唯一的标识,即唯一的地址,这意味着长度不为零” –是,但错误。对象有地址,但是指向对象的指针可以指向随机存储器。 “如果您要求零字节,那么实际的内存量将以静默方式增加”-不确定。运算符[]还将数组的大小存储在某个位置(请参见isocpp.org/wiki/faq/freestore-mgmt#num-elems-in-new-array)。因此,如果实现通过数据分配字节数,则只需分配字节数和0字节的数据,就返回1-past-last指针。
– Steed
16年4月13日在12:31
分配内存的长度为非零,而不是可用内存的长度为非零。我的观点是,指向两个不同对象的两个指针不应指向同一地址。
– ChristW
16-4-13在13:50
标准(open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf)确实表示(3.7.4.1/2),对运算符new []的不同调用应返回不同的指针。顺便说一句,新表达式具有一些附加规则(5.3.4)。我找不到任何线索,实际上需要分配0号的新内容。抱歉,我不满意,因为我发现您的答案不是回答问题,而是提供一些有争议的陈述。
– Steed
16年4月13日在15:31
@steed可以使用地址空间隐式计算存储块长度的内存。例如,对于零大小的数组,new []实现有可能返回没有动态内存映射的范围内的地址,因此实际上不使用任何内存(使用地址空间时)
– pqnet
17-10-20在16:00
@pqnet:高质量的实现应该允许无限数量的新/删除周期,不是吗?在具有64位指针的平台上,可以合理地说计算机执行while(!exitRequested){char * p = new char [0];删除[] p;没有循环指针的循环在可能耗尽地址空间之前会崩溃,但是在具有32位指针的平台上,这将是一个不太合理的假设。
–超级猫
18年6月27日在21:30
#4 楼
是的,将0
大小的块分配给new
是完全合法的。您根本无法做任何有用的事情,因为没有有效的数据可供您访问。 int[0] = 5;
是非法的。但是,我相信该标准允许
malloc(0)
之类的东西返回NULL
。无论从分配返回的指针如何,您仍然需要
delete []
也是。评论
关于malloc,您是对的-它是实现定义的。这通常被视为功能不当。
– anon
09年7月6日在13:49
我想一个有趣的问题是:如果给定大小为0,则新的nothrow版本是否可以返回NULL?
–埃文·特兰(Evan Teran)
09年7月6日在13:52
至少通过标准,new和malloc的语义没有以任何方式链接。
– anon
09年7月6日在13:53
并不是说他们是...只是专门询问了new的nohrow版本。
–埃文·特兰(Evan Teran)
09年7月6日在13:55
@Evan-new的nothrow版本仅在请求失败时返回null-不仅在请求大小为0时@Neil-没有规范地链接-而是通过意图链接(即new运算符可以在malloc方面实现,而在其他方面则不能实现解决方法)-请参阅我在答案中包含的脚注
–Faisal Vali
09年7月6日在14:07
#5 楼
奇怪的是,C ++要求new运算符即使请求零字节也要返回合法指针。 (要求这种听起来很奇怪的行为简化了语言中的其他内容。)
我发现有效的C ++第三版在“条款51:编写时遵守约定”中这样说。新建和删除”。
#6 楼
我向您保证,新的int [0]自测试以来会占用您额外的空间。例如,
int **arr = new int*[1000000000];
显着小于
int **arr = new int*[1000000000];
for(int i =0; i < 1000000000; i++) {
arr[i]=new int[0];
}
第二个代码段的内存使用量减去第一个代码段的内存使用量是用于大量新int的内存[0]。
评论
+1非常有趣的问题-尽管我不确定在实际代码中有多重要。@Zifre:我是在求求好奇,但这在现实世界中可能很重要,例如当以某种方式计算已分配内存块的大小,并且计算结果可能为零时,则不需要直接添加例外以不分配零大小的块。因为应该分配并删除它们而不会出现错误(如果只有零尺寸的块不会被取消引用)。因此,通常可以更广泛地抽象出什么是存储块。
@ emg-2:在您的示例情况下,实际上并不重要,因为delete []在NULL指针上是完全合法的:-)。
它只是切线相关的-所以我在这里评论-但是C ++在许多方面确保了不同的对象具有唯一的地址...即使它们不需要显式存储。一个相关的实验将是检查一个空结构的大小。或该结构的数组。
详细说明Shmoopty的评论:尤其是在使用模板编程时(例如,策略类模板,如std :: allocator),在C ++中通常使用零尺寸的对象。通用代码可能需要动态分配此类对象,并使用指向它们的指针来比较对象身份。这就是为什么运算符new()返回零大小请求的唯一指针的原因。尽管重要性可能不那么重要,但同样的道理也适用于数组分配和运算符new []()。