C编程 - realloc应该多久使用一次?

时间:2017-12-04 02:04:41

标签: c dynamic-memory-allocation realloc

我对动态内存分配有疑问。

背景:我正在编写一个程序,该程序读取单词的文本文件并计算每个单词出现的频率(每行一个单词)。

这个特殊的函数读取文件,计算行和字符,然后动态地将内存分配给字符串指针数组,一个数组存储每行的字符数和字符串本身。 (其他部分与我的问题不太直接相关)。

问题:如果空间不足,我应该多久重新分配一次内存?我设置了一个常量(" 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);
}

2 个答案:

答案 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的方式存在一些错误,但这不是问题。