C ++中是否存在最大数组长度限制?

时间:2008-10-19 10:39:11

标签: c++ arrays

C ++中是否有数组的最大长度?

是C ++限制还是取决于我的机器?它可以调整吗?它取决于数组的类型吗?

我可以以某种方式突破该限制,还是必须寻找更好的信息存储方式?什么应该是最简单的方法?

我要做的是在数组中存储long long int,我在Linux环境中工作。我的问题是:如果我需要存储一个N长整数的数组,我需要做什么? 10位数?

我需要这个,因为我正在为学校编写一些加密算法(例如p-Pollard),然后点击这个整数和数组表示长度的墙。

12 个答案:

答案 0 :(得分:153)

有两个限制,既不是由C ++强制执行,也是由硬件强制执行。

第一个限制(绝不应该达到)由用于描述数组中索引的大小类型的限制(及其大小)设置。它由系统std::size_t可以采用的最大值给出。此数据类型应始终是系统的最大整数类型。

另一个限制是物理内存限制。数组中的对象越大,达到此限制的时间越早,因为内存已满。例如,给定大小 n vector<int>通常占用类型vector<char>的数组(减去一个小的常量值)的内存大约四倍。因此,在内存已满之前,vector<char>可能包含的项目多于vector<int>。本机C样式数组int[]char[]的计数相同。

此外,此上限可能受用于构造allocator的{​​{1}}类型的影响,因为vector可以随心所欲地管理内存。一个非常奇怪但仍然可以想象的分配器可以以一种对象的相同实例共享资源的方式来池化内存。这样,您可以将大量相同的对象插入到容器中,否则会耗尽所有可用内存。

除此之外,C ++不会强制执行任何限制。

答案 1 :(得分:152)

没有人提到堆栈框架的大小限制。

有两个地方可以分配内存:

  • 在堆上(动态分配的内存)。
    这里的大小限制是可用硬件和操作系统通过使用其他设备临时存储未使用的数据来模拟空间的能力的组合(将页面移动到硬盘上)。
  • 在堆栈上(本地声明的变量)。
    这里的大小限制是编译器定义的(具有可能的硬件限制)。如果您阅读编译器文档,通常可以调整此大小。

因此,如果您动态分配一个数组(限制很大,并由其他帖子详细描述。

int* a1 = new int[SIZE];  // SIZE limited only by OS/Hardware

或者,如果在堆栈上分配了数组,那么您将受到堆栈帧大小的限制。 N.B。向量和其他容器在堆栈中的存在量很小,但通常大部分数据都在堆上。

int a2[SIZE]; // SIZE limited by COMPILER to the size of the stack frame

答案 2 :(得分:13)

从实用而非理论的角度来看,在32位Windows系统上,单个进程可用的最大内存总量为2 GB。您可以通过使用具有更多物理内存的64位操作系统来突破限制,但无论是执行此操作还是寻找替代方案,都取决于您的预期用户及其预算。您也可以使用PAE扩展它。

数组的类型非常重要,因为许多编译器的默认结构对齐是8个字节,如果内存使用是一个问题,这是非常浪费的。如果您使用Visual C ++来定位Windows,请查看#pragma pack指令以克服此问题。

要做的另一件事是查看内存压缩技术可能对您有何帮助,例如稀疏矩阵,即时压缩等等......再次,这是高度依赖于应用程序的。如果您编辑帖子以提供有关阵列中实际内容的更多信息,您可能会获得更多有用的答案。

编辑:如果有关于您的确切要求的更多信息,您的存储需求似乎在7.6 GB到76 GB之间未压缩,这需要相当昂贵的64位盒作为数组存储在C ++内存中。它提出了一个问题,为什么要将数据存储在内存中,其中假设访问速度,并允许随机访问。将此数据存储在数组之外的最佳方法几乎取决于您希望如何访问它。如果您需要随机访问数组成员,对于大多数应用程序,往往会有一些方法可以对可能同时访问的数据块进行分组。例如,在大型GIS和空间数据库中,数据通常按地理区域进行平铺。在C ++编程术语中,您可以覆盖[]数组运算符,以根据需要从外部存储中获取部分数据。

答案 3 :(得分:4)

我同意上述内容,如果您使用

初始化数组
 int myArray[SIZE] 

然后SIZE受整数大小的限制。但是你可以总是malloc一块内存并有一个指向它的指针,只要malloc不返回NULL就可以大到你想要的那么大。

答案 4 :(得分:4)

总结答案,扩展答案,并直接回答您的问题:

不,C ++不对数组的尺寸施加任何限制。

但是由于阵列必须存储在存储器中的某处,因此计算机系统的其他部分所施加的与存储器相关的限制也适用。请注意,这些限制与数组的维度(=元素数量)没有直接关系,而是与大小(=占用的内存量)直接相关。数组的尺寸( D )和内存尺寸( S )不同,因为它们与单个元素占用的内存相关( E ): S = D * E

现在 E 取决于:

  • 数组元素的类型(元素可以更小或更大)
  • 内存对齐(为了提高性能,元素放在地址上,这些地址是某个值的倍数,这引入了 元素之间的'浪费空间'(填充)
  • 对象的静态部分的大小(在面向对象的编程中,相同类型的对象的静态组件仅存储一次,与此类相同类型的对象的数量无关)

另请注意,通常通过在堆栈上分配数组数据(作为自动变量:int t[N])或堆上(使用malloc() / {{1的动态位置)来获得与内存相关的不同限制或者使用STL机制),或者在进程内存的静态部分(作为静态变量:new)。即使在堆上进行分配,您仍然需要堆栈上的少量内存来存储对堆分配的内存块的引用(但这通常可以忽略不计)。

static int t[N]类型的大小对程序员没有影响(我假设程序员使用size_t类型进行索引,因为它是为它设计的),因为编译器提供程序必须size_t到它整数类型足以满足给定平台体系结构可能的最大内存量。

内存大小限制的来源来自

  • 进程可用的内存量(对于32位应用程序,限制为2 ^ 32字节,即使在64位操作系统内核上也是如此),
  • 进程内存的划分(例如,为堆栈或堆设计的进程内存量),
  • 物理内存的碎片(许多分散的小空闲内存碎片不适用于存储一个整体结构),
  • 物理内存量,
  • 和虚拟内存量。

它们不能在应用程序级别“调整”,但您可以自由使用不同的编译器(更改堆栈大小限制),或将应用程序移植到64位,或将其移植到另一个操作系统,或更改(虚拟物理?)机器的物理/虚拟内存配置。

将所有上述因素视为外部干扰并因此作为运行时错误的可能来源,并仔细检查和响应程序代码中与内存分配相关的错误,这种情况并不罕见(甚至可取)。 /> 最后:虽然C ++没有施加任何限制,但在运行代码时仍需检查与内存相关的不利条件......: - )

答案 5 :(得分:3)

正如许多优秀的答案所指出的,有很多限制取决于您的C ++编译器版本,操作系统和计算机特性。但是,我建议在Python上使用以下脚本来检查计算机的限制。

它使用二进制搜索,并在每次迭代检查中是否可以通过创建尝试创建大小数组的代码来实现中间大小。脚本尝试编译它(抱歉,这部分仅适用于Linux)并根据成功调整二进制搜索。看看:

import os

cpp_source = 'int a[{}]; int main() {{ return 0; }}'

def check_if_array_size_compiles(size):
        #  Write to file 1.cpp
        f = open(name='1.cpp', mode='w')
        f.write(cpp_source.format(m))
        f.close()
        #  Attempt to compile
        os.system('g++ 1.cpp 2> errors')
        #  Read the errors files
        errors = open('errors', 'r').read()
        #  Return if there is no errors
        return len(errors) == 0

#  Make a binary search. Try to create array with size m and
#  adjust the r and l border depending on wheather we succeeded
#  or not
l = 0
r = 10 ** 50
while r - l > 1:
        m = (r + l) // 2
        if check_if_array_size_compiles(m):
                l = m
        else:
                r = m

answer = l + check_if_array_size_compiles(r)
print '{} is the maximum avaliable length'.format(answer)

您可以将其保存到您的计算机并启动它,它将打印您可以创建的最大尺寸。对于我的机器,它是2305843009213693951。

答案 6 :(得分:2)

我不认为有一件事在前面的答案中提到过。

当人们在设计中使用这些东西时,我总是感觉到重构意义上的“难闻的气味”。

从效率的角度和性能的角度来看,这是一个巨大的数组,可能不是表示数据的最佳方式。

欢呼声,

罗布

答案 7 :(得分:2)

如果您必须处理大型数据,则需要将其拆分为可管理的块。它不会全部适合任何小型计算机的内存。你可以 从磁盘加载一部分数据(无论合理的适合),执行计算和更改,将其存储到磁盘,然后重复直到完成。

答案 8 :(得分:1)

尽管所有当前答案都是令人讨厌的非特定性内容,但它们大多数都是正确的,但有许多警告,并不总是提及。要点是,您有两个上限,只有一个上限是实际定义的,因此YMMV

1。编译时限制

基本上,编译器会允许什么。对于x64 Windows 10机器上的Visual C ++ 2017,这是我在产生2GB限制之前的编译时最大限制,

unsigned __int64 max_ints[255999996]{0};

如果我改为这样做,

unsigned __int64 max_ints[255999997]{0};

我会得到:

  

Error C1126 automatic allocation exceeds 2G

我不确定2G与255999996 / 7的关系。我用两个谷歌搜索,唯一能找到的可能是关于precision issue with dc的* nix问答。无论哪种方式,您尝试填充哪种类型的int数组似乎都无关紧要,可以分配多少个元素并不重要。

2。运行时限制

您的堆栈和堆有其自身的局限性。这些限制都是根据可用系统资源以及应用程序本身的“繁重程度”而变化的值。例如,使用我当前的系统资源,我可以运行它:

int main()
{
    int max_ints[257400]{ 0 };
    return 0;
}

但是,如果我稍微调整一下...

int main()
{
    int max_ints[257500]{ 0 };
    return 0;
}

Bam!堆栈溢出!

  

Exception thrown at 0x00007FF7DC6B1B38 in memchk.exe: 0xC00000FD:   Stack overflow (parameters: 0x0000000000000001, 0x000000AA8DE03000).   Unhandled exception at 0x00007FF7DC6B1B38 in memchk.exe: 0xC00000FD:   Stack overflow (parameters: 0x0000000000000001, 0x000000AA8DE03000).

只是为了详细说明应用程序点的整体重量,这很不错:

int main()
{
    int maxish_ints[257000]{ 0 };
    int more_ints[400]{ 0 };
    return 0;
}  

但这会导致堆栈溢出:

int main()
{
    int maxish_ints[257000]{ 0 };
    int more_ints[500]{ 0 };
    return 0;
}  

答案 9 :(得分:1)

我很惊讶这里没有提到max_size()std::vector成员函数。

“由于系统或库的实现限制,返回容器能够容纳的最大元素数,即最大容器的std :: distance(begin(),end())。”

我们知道std::vector是作为引擎盖下的动态数组实现的,因此max_size()应该非常接近 dynamic 的最大长度在您计算机上的阵列。

以下程序将为各种数据类型建立一个大约最大数组长度的表。

#include <iostream>
#include <vector>
#include <string>
#include <limits>

template <typename T>
std::string mx(T e) {
    std::vector<T> v;
    return std::to_string(v.max_size());
}

std::size_t maxColWidth(std::vector<std::string> v) {
    std::size_t maxWidth = 0;

    for (const auto &s: v)
        if (s.length() > maxWidth)
            maxWidth = s.length();

    // Add 2 for space on each side
    return maxWidth + 2;
}

constexpr long double maxStdSize_t = std::numeric_limits<std::size_t>::max();

// cs stands for compared to std::size_t
template <typename T>
std::string cs(T e) {
    std::vector<T> v;
    long double maxSize = v.max_size();
    long double quotient = maxStdSize_t / maxSize;
    return std::to_string(quotient);
}

int main() {
    bool v0 = 0;
    char v1 = 0;

    int8_t v2 = 0;
    int16_t v3 = 0;
    int32_t v4 = 0;
    int64_t v5 = 0;

    uint8_t v6 = 0;
    uint16_t v7 = 0;
    uint32_t v8 = 0;
    uint64_t v9 = 0;

    std::size_t v10 = 0;
    double v11 = 0;
    long double v12 = 0;

    std::vector<std::string> types = {"data types", "bool", "char", "int8_t", "int16_t",
                                      "int32_t", "int64_t", "uint8_t", "uint16_t",
                                      "uint32_t", "uint64_t", "size_t", "double",
                                      "long double"};

    std::vector<std::string> sizes = {"approx max array length", mx(v0), mx(v1), mx(v2),
                                      mx(v3), mx(v4), mx(v5), mx(v6), mx(v7), mx(v8),
                                      mx(v9), mx(v10), mx(v11), mx(v12)};

    std::vector<std::string> quotients = {"max std::size_t / max array size", cs(v0),
                                          cs(v1), cs(v2), cs(v3), cs(v4), cs(v5), cs(v6),
                                          cs(v7), cs(v8), cs(v9), cs(v10), cs(v11), cs(v12)};

    std::size_t max1 = maxColWidth(types);
    std::size_t max2 = maxColWidth(sizes);
    std::size_t max3 = maxColWidth(quotients);

    for (std::size_t i = 0; i < types.size(); ++i) {
        while (types[i].length() < (max1 - 1)) {
            types[i] = " " + types[i];
        }

        types[i] += " ";

        for  (int j = 0; sizes[i].length() < max2; ++j)
            sizes[i] = (j % 2 == 0) ? " " + sizes[i] : sizes[i] + " ";

        for  (int j = 0; quotients[i].length() < max3; ++j)
            quotients[i] = (j % 2 == 0) ? " " + quotients[i] : quotients[i] + " ";

        std::cout << "|" << types[i] << "|" << sizes[i] << "|" << quotients[i] << "|\n";
    }

    std::cout << std::endl;

    std::cout << "N.B. max std::size_t is: " <<
        std::numeric_limits<std::size_t>::max() << std::endl;

    return 0;
}

在我的macOS(clang版本5.0.1)上,我得到以下信息:

|  data types | approx max array length | max std::size_t / max array size |
|        bool |   9223372036854775807   |             2.000000             |
|        char |   9223372036854775807   |             2.000000             |
|      int8_t |   9223372036854775807   |             2.000000             |
|     int16_t |   9223372036854775807   |             2.000000             |
|     int32_t |   4611686018427387903   |             4.000000             |
|     int64_t |   2305843009213693951   |             8.000000             |
|     uint8_t |   9223372036854775807   |             2.000000             |
|    uint16_t |   9223372036854775807   |             2.000000             |
|    uint32_t |   4611686018427387903   |             4.000000             |
|    uint64_t |   2305843009213693951   |             8.000000             |
|      size_t |   2305843009213693951   |             8.000000             |
|      double |   2305843009213693951   |             8.000000             |
| long double |   1152921504606846975   |             16.000000            |

N.B. max std::size_t is: 18446744073709551615

ideone gcc 8.3上,我得到:

|  data types | approx max array length | max std::size_t / max array size |
|        bool |   9223372036854775744   |             2.000000             |
|        char |   18446744073709551615  |             1.000000             |
|      int8_t |   18446744073709551615  |             1.000000             |
|     int16_t |   9223372036854775807   |             2.000000             |
|     int32_t |   4611686018427387903   |             4.000000             |
|     int64_t |   2305843009213693951   |             8.000000             |
|     uint8_t |   18446744073709551615  |             1.000000             |
|    uint16_t |   9223372036854775807   |             2.000000             |
|    uint32_t |   4611686018427387903   |             4.000000             |
|    uint64_t |   2305843009213693951   |             8.000000             |
|      size_t |   2305843009213693951   |             8.000000             |
|      double |   2305843009213693951   |             8.000000             |
| long double |   1152921504606846975   |             16.000000            |

N.B. max std::size_t is: 18446744073709551615

应注意,这是理论上的限制,并且在大多数计算机上,您将在达到此限制之前耗尽内存。例如,我们看到,对于char上的类型gcc,元素的最大数量等于std::size_t的最大值。尝试this时,出现错误:

prog.cpp: In function ‘int main()’:
prog.cpp:5:61: error: size of array is too large
  char* a1 = new char[std::numeric_limits<std::size_t>::max()];

最后,正如@MartinYork所指出的,对于静态数组,最大大小受堆栈大小限制。

答案 10 :(得分:0)

正如已经指出的那样,数组大小受硬件和操作系统(man ulimit)的限制。但是,您的软件可能仅受您的创造力限制。例如,您可以将“阵列”存储在磁盘上吗?你真的需要很长的时间吗?你真的需要一个密集阵列吗?你甚至需要一个阵列吗?

一个简单的解决方案是使用64位Linux。即使您的阵列没有足够的RAM,操作系统也可以像您一样分配内存,因为您的进程可用的虚拟内存可能比物理内存大得多。如果您确实需要访问阵列中的所有内容,则相当于将其存储在磁盘上。根据您的访问模式,可能有更有效的方法(例如:使用mmap(),或者只是将数据按顺序存储在文件中(在这种情况下,32位Linux就足够了)。)

答案 11 :(得分:0)

我会通过制作2d动态数组来解决这个问题:

long long** a = new long long*[x];
for (unsigned i = 0; i < x; i++) a[i] = new long long[y];

此处更多信息https://stackoverflow.com/a/936702/3517001