QList 的最大大小是多少?

What is QList's maximum size?

本文关键字:多少 QList      更新时间:2023-10-16

有没有人遇到过QList的最大大小?

我有一个指向我的对象的指针的 QList,发现它在到达第 268,435,455 个项目(正好是 28 位)时静默抛出错误。我本来希望它至少具有 31 位的最大大小(减去一位,因为 size() 返回有符号整数),或者在我的 63 位计算机上具有 64 位的最大大小,但事实似乎并非如此。我已经通过在一个最小的示例中通过在计数循环中执行QList<void*> mylist; mylist.append(0);来确认这一点。

为了重申这个问题,QList 的实际最大大小是多少?如果它实际上不是 2^32-1,那为什么?有解决方法吗?

我正在运行Windows 64位版本的Qt 4.8.5 for MSVC2010。

虽然其他答案对解释问题进行了有用的尝试,但它们都没有真正回答问题或错过重点。感谢大家帮助我追踪问题。

正如 Ali Mofrad 所提到的,当 QList 无法在我的QList::append(MyObject*)调用中分配额外空间时,抛出的错误是一个std::bad_alloc错误。这是Qt源代码中发生的地方:

qlist.cpp: line 62:
static int grow(int size)         //size = 268435456
{
    //this is the problem line
    volatile int x = qAllocMore(size * sizeof(void *), QListData::DataHeaderSize) / sizeof(void *);
    return x;                     //x = -2147483648
}
qlist.cpp: line 231:
void **QListData::append(int n)   //n = 1
{
    Q_ASSERT(d->ref == 1);
    int e = d->end;
    if (e + n > d->alloc) {
        int b = d->begin;
        if (b - n >= 2 * d->alloc / 3) {
            //...
       } else {
            realloc(grow(d->alloc + n));    //<-- grow() is called here
        }
    }
    d->end = e + n;
    return d->array + e;
}

grow()中,请求的新大小(268,435,456)乘以sizeof(void*)(8)来计算新内存块的大小,以适应不断增长的QList。问题是,如果它是无符号的int32,则268435456*8等于+2,147,483,648,或者对于已签名的int32,则等于-2,147,483,648,这是作系统上的grow()返回的内容。因此,当 std::realloc() 在 QListData::realloc(int) 中被调用时,我们试图增长到负大小。

正如ddriver建议的那样,这里的解决方法是使用QList::reserve()来预先分配空间,防止我的 QList 不得不增长。

简而言之,QList 的最大大小为 2^28-1 个项目,除非您预先分配,在这种情况下,最大大小确实是预期的 2^31-1。

更新(2020 年 1 月):这似乎在 Qt 5.5 中发生了变化,因此 2^28-1 现在是 QList 和 QVector 允许的最大大小,无论您是否提前预订。可惜了。

有没有人遇到过QList的最大大小?我有一个指向我的对象的指针的 QList,发现它在到达第 268,435,455 个项目(正好是 28 位)时静默抛出错误。我本来希望它至少具有 31 位的最大大小(减去一位,因为 size() 返回有符号整数),或者在我的 63 位计算机上具有 64 位的最大大小,但事实似乎并非如此。

存储在 int 中的理论最大正数是 2^31 - 1。指针的大小为 4 个字节(对于 32 位计算机),因此它们的最大可能数量为 2^29 - 1。将数据追加到容器会增加堆内存的碎片,因此您可能只能分配一半可能的内存。尝试改用 reserve() 或 resize()。

此外,Win32 对内存分配有一些限制。因此,在没有特殊选项的情况下编译的应用程序不能分配超过此限制(1G 或 2G)。

你确定这个巨大的容器吗?优化应用程序更好吗?

QList将其元素存储在void *数组中。

因此,包含 228 个项目的列表,其中每个项目都是一个void *,在 32 位机器上长度为 230 字节,在 64 位计算机上长度为 231 字节。我怀疑你能请求这么大的连续内存。

为什么要分配如此庞大的清单?你确定你真的需要它吗?


void * 元素数组支持的想法是因为列表中的多个操作可以移动到非模板化代码,从而减少生成的代码量。

QList 将项目直接存储在 void * 数组中,如果类型足够小(即 sizeof(T) <= sizeof(void*) ),如果可以通过 memmove 在内存中移动类型。否则,每个项目将通过 new 在堆上分配,数组将存储指向这些项目的指针。一组类型特征用于确定如何处理每种类型,请参阅Q_DECLARE_TYPEINFO。

虽然从理论上讲,这种方法听起来很有吸引力,但在实践中:

  • 对于所有小于 void * 的基元类型(char;int 和 float 在 64 位上等),您浪费了数组中 50% 到 75% 的分配空间
  • 对于所有大于 void * 的可移动类型(32 位上的双倍、QVariant 等),您需要为列表中的每个项目(加上数组本身)支付堆分配
  • 费用
  • QList代码通常不如QVector one优化
  • 如今的编译器在合并模板实例化方面做得很好,因此这种设计的原始原因丢失了。

今天,坚持使用QVector是一个更好的主意。不幸的是,Qt API 在任何地方都暴露了 QList,并且无法更改它们(我们需要 C++11 将 QList 定义为 QVector 的模板别名......

我使用 qt32 在 4GB RAM 的 Ubuntu 4.8.6 中对此进行了测试。我的最大大小是 268,435,450

我使用 qt7 在 Windows 32 位和 4GB RAM 中对此进行了测试,4.8.4。我的最大大小是 134,217,722

发生此错误:"std::bad_alloc"

#include <QCoreApplication>
#include <QDebug>
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QList<bool> li;
    for(int i=0; ;i++)
    {
        li.append(true);
        if(i>268435449)
            qDebug()<<i;
    }
    return a.exec();
}

输出为 :

268435450

在抛出"std::bad_alloc"
实例后终止调用什么(): 标准::bad_alloc