为什么在C#中允许使用“long”作为数组长度?

18

我想尝试分配一个大小为 4 亿字节的数组,这是我的 C# 代码:

long size = 4 * 1000;
size *= 1000;
size *= 1000;
byte[] array = new byte[size];

这段代码在包含new的行中出现System.OverflowException错误。原来Length返回的是int,所以数组长度也受到了int可存储的限制。

那么为什么没有编译时错误,而使用long作为分配数组元素的数量却是允许的呢?


6
Array.CreateInstance()方法会显示更有意义的错误信息:"不支持大于2GB的数组。" - Bradley Smith
所以你正在分配3.7 GB的内存。你的机器上安装了多少RAM? :D - konqi
JoSo,物理内存的数量在这里并不重要。 - Joey
2
@Joso:这是一个64位进程,所以由于分页技术的支持,如果有足够的磁盘空间,它可以分配3.7 ih。 - sharptooth
1
即使是分页也有其限制。假设有1024 MB的物理内存,Windows通常会将分页文件限制为1536 MB。结合起来,您可以分配2560 MB的内存。由于Windows将上限页文件大小设置为物理内存的1.5倍,因此我会说物理内存可能不是限制因素,但它确实是一个因素。 - konqi
3
JoSo,(a)理智的人不会在只有1 GiB内存的情况下使用64位Windows;(b)如今内存非常便宜,因此少于8 GiB的内存几乎没有意义;(c)你看过答案了吗?问题完全是其他方面的原因。 - Joey
5个回答

23

这是因为规范在第7.6.10.4节中有所说明:

表达式列表中的每个表达式必须是类型为intuintlongulong,或者可以隐式转换为其中一个或多个类型。

这很可能是为了更容易地允许创建大于2 GiB的数组,尽管它们目前不受支持(但一旦CLR进行此类更改,就不需要语言更改)然而Mono支持这个功能.NET 4.5显然也将允许更大的数组

顺便提一下关于数组长度是int的问题:还有一个返回longLongLength。这在.NET 1.1中就有了,并且很可能是一种未来的预防性变化。


1
+1 对于4.5的东西,你说得很好,但愿我永远不会在我们的应用程序中看到它被启用... - Adam Houldsworth
1
看起来即使进行了4.5的更改,问题中的代码仍将失败,因为它说:“任何单个维度中的最大索引为2,147,483,591(0x7FFFFFC7)适用于字节数组和单字节结构的数组,以及2,146,435,071(0X7FEFFFFF)适用于其他类型。” - Eren Ersönmez
确实。你可以将其变成多维数组来解决这个问题。但那也很糟糕。 - Joey
@ErenErsönmez 你写道“它说……”,它在哪里说的? - Mishax
@Mishax:在我回答中包含的第二个链接中。 - Joey

11

为什么允许使用long作为数组长度?

答案是:在.NET中,long代表Int64。

根据规范,数组的索引可以是Int64。

第二个问题:为什么会显示OverflowException错误?

因为任何单个对象不能分配超过2GB的内存空间。


3
在提出这样的声明时(除非它们是显而易见的,例如 longSystem.Int64 的别名),最好附上参考文献来支持它们。 - Joey
1
我不同意你的第二点。如果是内存不足,应该期望抛出OutOfMemoryException而不是OverflowException。根据规范,只有在长度小于零时才会引发OverflowException。 - Mishax
1
@Mishax:这不是内存问题,只是单个对象不能超过那么大。 - Joey
1
@Joey,你可能是对的,但我指的是mdkamruzzaman对OverflowException的解释。在C#语言规范中,它指出如果任何维度长度说明符小于零,则可能会引发OverflowException异常。这就是原因,也是唯一的原因。我在语言规范中找不到任何关于2GB限制的提及。这里有一些讨论:https://dev59.com/hG7Xa4cB1Zd3GeqPqX-K - Mishax

7
这是CLR的限制,单个对象不能超过2GB,包括数组: C#中的大型数组OutOfMemoryException 无论是32位还是64位操作系统都一样。尽管如此,这并不妨碍您在总量上使用更多,只是不能用于一个对象。
这是一个运行时错误,因为如果将long(或其他初始化值)保持在范围内,它就可以工作。
您可以使用所有整数类型(sbyte、char、short、int和long)初始化数组-所有编译;无符号变体也可以工作。

4

在 .Net 4.5-4.6 中,有一种解决方案可以允许数组具有更大的尺寸。

<runtime>
    <gcAllowVeryLargeObjects enabled="true" />
</runtime>

请查看文档。

查看文档.


3

long是一种整型,因此可以用它来定义数组。异常的产生与使用long无关,而是由于构建一个太大的数组。

例如,以下代码可以正常运行:

long size = 20;
byte[] array = new byte[size];

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