我有一个简单(可行)的问题。在C中创建自己的动态数组实现时,最好是在数组接近其当前最大能力时提前分配内存(假设为10个元素),或者每当元素数量发生变化时重新分配内存?
我的意思是:为了表现,优雅或任何你想到的东西。
答案 0 :(得分:5)
通常的选择是将当前大小乘以大于1的固定数字(通常为1.5,因为2),这样可以分摊O(n)总分配和复制成本。
请注意,无法保证您可以增加阵列的大小,因此您可能需要将所有现有元素复制到新位置(realloc()
会自动为您执行此操作,但您仍然可以必须支付费用。)
答案 1 :(得分:4)
更多更好的性能,以尽可能少的时间重新分配。所以你可以这样做:
array = realloc(array, old_size * 2);
答案 2 :(得分:2)
通常最好以“起始”固定大小进行预分配,并在空间耗尽时根据增长因子重新分配。
根据您的需要,可以根据您正在处理的数据的典型使用情况确定起始大小和增长因子,或者您为数据创建的API可以允许调用者将起始大小和增长因子指定为部分初始化/创建调用。
起始大小应该是基于典型用法的数字。典型用法是帮助您选择尺寸的重要因素,以便您:A)不要通过选择“太大”的起始尺寸来浪费空间,并且B)不要使用太小而不是重新分配的起始尺寸在达到目标典型尺寸之前是必要的。
当然,典型的大小是一个神奇的数字。确定典型大小的一种方法是使用各种数据集运行一些测试,并收集启动大小,重新分配数量和数据的最小/最大内存使用量的统计信息。您可以对结果进行平均以获得可用的典型起始大小。
对于生长因子,x1.5或x2生长因子是常见的。这是您可以使用测试统计量和起始大小来衡量的。
需要考虑的另一件事是,您需要小心管理对动态可调整大小的数据的引用,因为realloc()会在需要时将数据重定位到内存中。这意味着如果您存储了动态可调整大小的数组的第一个元素的地址,那么在调用realloc之后该地址可能无效。这可以通过围绕自定义数据类型的API包装来管理,该自定义数据类型分发索引而不是内存地址,并且可以在需要时将索引解析为元素的当前地址。
答案 3 :(得分:1)
在堆上分配内存总是很昂贵。逐个元素地完成它根本不可能。
答案 4 :(得分:1)
您可以定义如下结构:
/** Dynamic array */
typedef struct __darray {
int* array; /** Array */
int size; /** Array size */
int cap; /** Capacity */
} darray;
尺寸< =容量。
如果您的尺寸>动态添加新元素测试容量。 如果为true则执行内存重新分配。 公式(取自JDK的ArrayList实现)是:
(jobs here...)
[your_darray]->capacity+=[your_darray]->capacity*3/2+1;
[your_darray]->array=(int*)realloc([your_darray]->array,capacity*sizeof(int));
(jobs here...)
如果从动态数组中删除(足够)元素,请不要忘记再次收缩“int *”。
答案 5 :(得分:0)
如果这个动态数组实现意图在很多上下文中使用,那么最好将对预测性额外分配的控制权交给用户,而不是将其嵌入到库中。
答案 6 :(得分:0)
更新的内存分配策略是Solaris和Linux采用的Slab Allocator。 Memcached也使用了this FAQ entry记录的slab分配器。