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

C编程-应该多久使用一次realloc?,c,dynamic-memory-allocation,realloc,C,Dynamic Memory Allocation,Realloc,我有一个关于动态内存分配的问题 上下文:我正在编写一个程序,它读取一个单词文本文件,并计算每个单词出现的频率(每行一个单词) 此特定函数读取文件,计算行数和字符数,然后动态地将内存分配给字符串指针数组,该数组存储每行的字符数和字符串本身。(其他部分与我的问题不太直接相关) 问题:如果空间不足,我应该多久重新分配一次内存?我设置了一个常量(“memstart”)来设置初始内存分配值。在下面的代码段中,我为超过“memstart”值的每一行设置了realloc。如果重新分配一个更大的内存块,而不是每

我有一个关于动态内存分配的问题

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

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

问题:如果空间不足,我应该多久重新分配一次内存?我设置了一个常量(“memstart”)来设置初始内存分配值。在下面的代码段中,我为超过“memstart”值的每一行设置了realloc。如果重新分配一个更大的内存块,而不是每次增加1个“变量类型”,程序的处理速度会更快吗

对于这样的事情,最好的做法是什么

代码剪切:

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);
}

经典的答案是每次翻倍。重要的一点是,每次将数组大小乘以某个因子,而不是每次都添加额外的空间

每次重新分配可能需要将以前的阵列复制到新阵列中。我们想尽量减少这些副本。如果我们将添加
n
项,并且我们从一个大小为
a
的数组开始,每次重新分配增加一个系数
r
,以值
n
结束,(重新)分配的顺序将是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空。那可能没关系。完成后,您始终可以缩小分配,并知道最终大小


正如在另一个答案中指出的那样,在调用
realloc()
的方式中有几个错误,但这不是问题。

问题是关于调用
realloc
的频率,看看OP的代码,我认为最好从应该如何安全地执行开始

C11标准规定(n1570草案,§7.22.3.5,realloc功能,强调矿山):

简介

#包括

void*realloc(void*ptr,size\u t size)

说明
realloc函数释放ptr指向的旧对象,并返回一个指向新对象的指针,该对象的大小由size指定。新对象的内容应与解除分配前的旧对象的内容相同,以新旧尺寸中较小者为准。新对象中超出旧对象大小的任何字节都有不确定的值。
如果ptr是空指针,realloc函数的行为与指定大小的malloc函数类似。(...). 如果无法为新对象分配内存,则旧对象不会释放,其值也不会改变。
返回
realloc函数返回指向新对象的指针(可能与指向旧对象的指针具有相同的值),如果无法分配新对象,则返回空指针

现在让我们从这个问题中考虑这个代码片段,在这里代码< > SZ < /C> >被声明为<代码> 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;   
size\u t new\u size=/*随便什么,比如说*/size+SOME\u COSTANT+size/2;
void*tmp=realloc(ptr,新尺寸*sizeof*ptr);
if(tmp==NULL){
/*处理错误,例如使用perror记录消息,返回NULL
(如果这是在函数中)或放弃,但请记住
realloc不会在发生故障时使“ptr”失效或释放*/
退出(退出失败);
}

ptr=tmp;//通常情况下,容器会以某个恒定因子增长。比如说1.5。也就是说,每次您需要调整它的大小时,您都会使其容量
1.5x
为当前容量。当增加分配时,一个好的一般策略是从合理大小的分配开始,并根据需要加倍。完成增长后,可以通过最后调用
realloc()
将分配修剪为最终大小。您应该重新分配
log
次数。
realloc
(比以前大)应使用至少与您打算踩过上一个大小的最后一个字节的次数相同的次数。再少一点,你就有麻烦了。就几个小毛病
realloc
返回
void*
,因此
void*tmp=realloc(..
是所需的全部。
if(tmp==NULL)
不要释放指针所有以前的数据仍然由
ptr
指向,只是不要分配
ptr=tmp;
。最后一点,
“*”
与变量一起使用,而不是与类型一起使用。(例如
int*a,b,c;
当然不会使
b
c
指针)@DavidC.Rankin 1)很好,我错过了。2) 我之所以释放,正是因为数据仍然是指向的,因为在我退出的代码段中,我通常在函数中这样做,并返回(并检查,以处理其他释放的资源)NULL,但我明白你的意思。3) 我知道;)这是我经常使用的一种风格,因为我通常不会在语句中声明一个以上的变量,但这可能会误导,我明白你的意思。谢谢。这是非常有益的。