QList 的最大大小是多少?
What is QList's maximum size?
有没有人遇到过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
- 复制列表初始化的隐式转换的等级是多少
- while循环中while循环的时间复杂度是多少
- 如何检查一个c++字符串中有多少相同的字符/数字
- C++有多少类型的循环
- 求出有多少个数字是完美平方,而sqrt()是L,R范围内的素数
- 如何检查QList中是否存在值
- 在条件变量中触发错误信号的频率是多少
- 函数的时间复杂度是多少?
- 必须为 C++20 协程帧保留多少内存?
- 对于四轴飞行器,PID中I控制器的理想值应该是多少
- 访问类 QList 指针的成员
- C++,数组有多少个地址?
- 在C++中使用并行化的预期速度是多少(不是 OpenMp,而是 <thread>)
- 在 Linux 中存储区域设置名称的缓冲区大小应该是多少?
- 在内存不足之前,我可以声明多少个 const 变量?
- 可以读入进程内存的最大块大小是多少?
- 如何在不知道C++中有多少可选参数的情况下在循环中使用va_arg?
- 在二维向量或数组中可以存储的最大元素数是多少?
- C++ template for QList
- QList 的最大大小是多少?