我对动态内存分配有疑问。
背景:我正在编写一个程序,该程序读取单词的文本文件并计算每个单词出现的频率(每行一个单词)。
这个特殊的函数读取文件,计算行和字符,然后动态地将内存分配给字符串指针数组,一个数组存储每行的字符数和字符串本身。 (其他部分与我的问题不太直接相关)。
问题:如果空间不足,我应该多久重新分配一次内存?我设置了一个常量(" memstart")来设置初始内存分配值。在下面的代码片段中,我重新分配了超过" memstart"值的每一行。如果重新分配更大的内存块而不是将内存空间增加1"变量类型"程序会更快地处理吗?每一次?
对于这样的事情,最佳做法是什么?
Code Snip:
int read_alloc(FILE* fin, FILE *tmp, char **wdp, int *sz){
int line_cnt= 0, chr, let=1;
do{
chr=getc(fin);
let++;
//count characters
if(chr!=EOF){
chr=tolower(chr);
fputc(chr, tmp);
}
//convert to lcase and write to temp file
if ('\n' == chr || chr==EOF){
sz[(line_cnt)]=((let)*sizeof(char)); //save size needed to store string in array
*(wdp+(line_cnt))=malloc((let)*sizeof(char)); //allocate space for the string
if ((line_cnt-1) >= memstart){
realloc(wdp, (sizeof(wdp)*(memstart+line_cnt))); //if more space needed increase size
realloc(sz, (sizeof(sz)*(memstart+line_cnt)));
}
line_cnt++;
let=1;
}
} while (EOF != chr);
return (line_cnt);
}
答案 0 :(得分:7)
虽然问题是realloc
应该多久调用一次,看看OP的代码,但我认为最好从安全开始。
C11标准规定(n1570草案,§7.22.3.5, realloc函数,强调我的):
概要
#include <stdlib.h>
void *realloc(void *ptr, size_t size);
<强>描述强>
realloc函数释放ptr指向的旧对象,并返回指向大小指定大小的新对象的指针。新对象的内容应与旧对象的内容相同在解除分配之前,新旧尺寸中的较小者。新对象中超出旧对象大小的任何字节都具有不确定的值 如果ptr是空指针,则realloc函数的行为类似于指定大小的malloc函数。 (......)。 如果无法分配新对象的内存,则不会释放旧对象,并且其值不会更改。
的返回强>
realloc函数返回一个指向新对象的指针(可能与指向旧对象的指针具有相同的值),如果无法分配新对象,则返回或空指针。
现在让我们考虑问题的这个片段,其中sz
被声明为int* sz;
realloc(sz, (sizeof(sz)*(memstart+line_cnt)));
返回值丢失,因此我们无法知道调用是否成功,如果调用成功,则sz
无效。此外,sizeof(sz)
是指针的大小,而不是指向类型(int
)的大小。
更安全(和更正确)的模式是:
size_t new_size = /* Whatever, let's say */ size + SOME_COSTANT + size / 2;
void *tmp = realloc(ptr, new_size * sizeof *ptr);
if ( tmp == NULL ) {
/* Deal with the error, e.g. log a message with perror, return NULL
(if this is in a function) or just give up, but remeber that
realloc doesn't invalidate nor free 'ptr' on failure */
exit(EXIT_FAILURE);
}
ptr = tmp; // <- on success, realloc invalidated ptr
size = new_size;
现在,要回答这个问题,只应在需要时调用realloc
,因为它涉及潜在的扩展系统调用。因此要么预先分配一大块,要么选择每次增加(或1.5倍)大小的增长策略。
值得注意的是,如果可能,操作系统可以执行重新分配而不复制原始数组的任何元素。
答案 1 :(得分:4)
经典答案是每次加倍but a factor of 1.5 might be better。重要的是,每次将数组大小乘以某个因子,而不是每次添加额外的空间。
每次重新分配都可能需要将之前的数组复制到新数组中。我们希望尽量减少这些副本。如果我们要添加n
项,并且我们从大小为a
的数组开始,则每次重新分配增加r
因子,以{{1}结束(重新)分配的顺序将是a,ar,ar ^ 2,ar ^ 3,...,n。该序列的总和是(nr-a)/(r-1)。因此总空间为O(n)。
假设我们从a开始,这次每次都添加r。序列是a,a + r,a + 2r,a + 3r,...,n。该序列的总和将是0.5 *((n ^ 2-a ^ 2)/ r + a + n)。在这种情况下,订单的总空间为O(n ^ 2)。更糟糕的是!
使用常数因子2,数组将在最坏的情况下1/2空。那可能没问题。您可以随时缩小分配,并了解最终尺寸。
正如另一个答案所指出的那样,你调用n
的方式存在一些错误,但这不是问题。