Realloc和一个函数或malloc和两个函数?

时间:2014-01-12 23:29:45

标签: c file function malloc realloc

我的文件有num行:每行包含一个数字。我想将每个数字保存到向量*vet中。哪两个版本更好?

[版本1] 我有两个功能:第一个用于计算num,第二个用于保存号码到*vet。我在malloc中使用main()分配内存。

#include <stdio.h>
#include <stdlib.h>

/* The first function counts lines number */
int count_line (int *num)
{
    FILE *fin;
    char buff[10];

    *num = 0;

    if ( !(fin = fopen("numbers.dat", "r")) )
        return 1;

    while ( fgets(buff, sizeof(buff), fin) )
        (*num)++;

    return fclose(fin);
}

/* The second function save numbers into a vector */
int save_numbers (int *vet)
{
    FILE *fin;
    int i=0;
    char buff[10];

    if ( !(fin = fopen("numbers.dat", "r")) )
        return 1;

    while ( fgets(buff, sizeof(buff), fin) )
    {
        sscanf (buff, "%d", &vet[i]);
        i++;
    }

    return fclose(fin);
}

int main ()
{
    int num, i, *vet;

    if ( count_line(&num) )
    {
        perror("numbers.dat");
        exit(1);
    }

    vet = (int *) malloc ( num * sizeof(int) );

    if ( save_numbers(vet) )
    {
        perror("numbers.dat");
        exit(2);
    }

    /* print test */
    for (i=0; i<num; i++)
        printf ("%d ", vet[i]);
    printf("\n");

    free(vet);

    return 0;
}

[版本2] 我只有一个功能:它使用realloc分配内存并将数字保存到*vet

#include <stdio.h>
#include <stdlib.h>

/* This function allocate memory
and save numbers into a vector */
int save_numbers (int **vet, int *num)
{
    FILE *fin;
    int i = 0;
    char buff[10];

    if ( !(fin = fopen("numbers.dat", "r")) )
        return 1;

    while ( fgets(buff, sizeof(buff), fin) )
    {
        *vet = (int *) realloc (*vet, (i+1) * sizeof(int) );
        sscanf (buff, "%d", &(*vet)[i]);
        i++;
    }

    *num = i;

    return fclose(fin);
}

int main ()
{
    int i, num, *vet = NULL;    

    if ( save_numbers(&vet, &num) )
    {
        perror("numbers.dat");
        exit(1);
    }

    /* print test */
    for (i=0; i<num; i++)
        printf ("%d ", vet[i]);
    printf("\n");

    free(vet);

    return 0;
}

此处的文件示例:http://pastebin.com/uCa708L0

2 个答案:

答案 0 :(得分:4)

作为A Person commented,磁盘I / O价格昂贵,因此版本2更好,因为它只读取文件一次。

虽然不好;一般来说,增加内存分配会增加二次成本。您应该计划在每次分配时将分配的空间量加倍,以摊销分配的成本。当你为数字N分配空间时,这可以避免将先前的N-1数字从一个地方复制到下一个地方的成本。这不会总是发生,但正式地,realloc()释放了它当前拥有的空间分配并分配新空间(但有时旧指针和新指针都是相同的)。

#include <stdio.h>
#include <stdlib.h>

/* This function allocate memory
and save numbers into a vector */
static int save_numbers(char const *file, int **vet, int *num)
{
    FILE *fin;
    int i = 0;
    int n_max = 0;
    char buff[10];

    if ((fin = fopen(file, "r")) == 0)
        return -1;

    while (fgets(buff, sizeof(buff), fin) != 0)
    {
        if (i >= n_max)
        {
            int n_new = (2 * n_max) + 2;
            void *v_new = (int *)realloc(*vet, n_new * sizeof(int));
            if (v_new == 0)
                return -1;
            *vet = v_new;
            n_max = n_new;
        }
        if (sscanf(buff, "%d", &(*vet)[i]) != 1)
            break;
        i++;
    }

    *num = i;

    /* Optionally release surplus space - if there is enough to warrant doing so */
    if (i + 8 < n_max)
    {
        void *v_new = realloc(*vet, i * sizeof(int));
        if (v_new == 0)
            return -1;
        *vet = v_new;
    }

    return fclose(fin);
}

int main(void)
{
    int i, num, *vet = NULL;    

    if (save_numbers("numbers.dat", &vet, &num))
    {
        perror("numbers.dat");
        exit(1);
    }

    /* print test */
    for (i = 0; i < num; i++)
        printf ("%d ", vet[i]);
    printf("\n");

    free(vet);

    return 0;
}

讨论

  

我试图理解代码,但可能我还是太棒了。

问题是什么?我引入新变量n_max来计算分配的行数;该数字最初为零。当读取新行时,代码检查以查看阵列中是否还有空槽(i> = n_max)。如果没有剩余空间,则(2 * n_max)+ 2计算(我通常使用序列2,6,14,30,或使用(2 + n_max)* 2作为序列4,12 ,28,60 - 两者都确保经常进行重新分配以进行测试)给出了一个新的非零大小。然后代码分配空间,在覆盖前一个指向已分配内存的指针之前检查分配是否成功,从而避免内存泄漏。如果一切正常,则分配新指针和新大小,并像以前一样继续或多或少地继续,但检查sscanf()是否有效。

  

但为什么int n_new = (2 * n_max) + 2;?为什么(2 * n_max) + 2;

因为2 * 0是0,它不会分配比分配0更多的空间。

  

[1]可能我不太了解realloc()的工作原理。为什么在void *v_new = (int *)realloc(*vet, n_new * sizeof(int)); *v_new*vet都有?

小漏洞;它应该是int *v_new = (int *)realloc(*vet, n_new * sizeof(int));,或者它应该是void *v_new = realloc(*vet, n_new * sizeof(int));,尽管它的书面作品。至于额外变量的原因,看看当n_new为6时的内存分配会发生什么。变量*vet保存唯一指向保存2个数字的原始数据的指针。如果分配失败但你已经写了*vet = (int *)realloc(*vet, n_new * sizeof(int));,那么你也没有机会释放其他2个号码 - 你不再有指向它们的指针。

一般来说,这个成语:

pointer = realloc(pointer, new_size);

是有风险的,因为它丢失了指向先前分配的指针。这就是为什么上面的代码将新指针保存到另一个变量中的原因。

另请注意,*中的void *v_new*中的*vet不同。在v_new的声明中,它表示该类型是指针。在赋值的RHS中,它是解引用运算符。

  

[2]为什么*vet = v_new;

将新指针保存到v_new后,一旦验证,就可以安全地分配到

  

[3]为什么if (i + 8 < n_max)?及其内容?

如果有足够的超额分配内存值得担心(8个整数是关于64位机器上有意义的最小值),那么可以选择释放剩余空间&#39 ;代码在块的末尾释放未使用的内存。标准并没有说realloc()在缩小数据时不会移动数据,尽管这是realloc()等非常罕见的实现,它在缩小时确实会移动数据。在该代码中省略&#39; realloc()返回NULL`检查是非常诱人的。

如果in_max的8之内(那个值为8的整数,或者通常为32个字节),则可能没有足够的空间值得释放。在64位系统上,最小分配通常是16个字节,即使您分配连续的单个字符,返回的指针通常也会相隔16个字节。因此,返回少于16个字节通常是完全无操作。返回32个字节更可能是可用的。这是一个判断调用,但是4或8是合理的数字,16个不会出现故障 - 或者你可以完全忽略过度分配。 (另一方面,如果将分配从1 GiB增加到2 GiB,然后从第二个GiB使用256个字节,则可能值得返回第二个GiB数据的剩余部分。)

  

[4]告诉我,如果我理解:我的版本2没问题,但是你的代码更好,因为它没有为每一个数字重新分配内存,但它分配了更多的内存。在第1个周期,它分配2*sizeof(int),在第2个周期分配6*sizeof(int),在第3个周期分配14*sizeof(int)等。然后,如果分配的内存太多,则免费那个if (i + 8 < n_max)。对?我明白了吗?

这非常正确。当然,&#39;周期&#39;你指的是每次读取数字时代码都不会分配。当代码读取第二个数字时,它不会分配任何内容;它第一次为2个数字分配了足够的空间。当它读取第三个数字时,分配变为6个数字,因此它会读取第四个到第六个数字而无需进行另一个分配。因此,它不需要6次分配来读取第六个数字,而是只进行了2次分配 - 相当节省。

  

[5]如何处理错误?例如,如果if (v_new == 0)为真(v_new为0),则函数返回-1而main返回perror("numbers.dat");,但它不是文件错误。

有各种方法可以做到这一点。我在对chux的评论回复中提到,我通常会单独报告内存错误;实际上,我通常也会在函数中报告文件处理错误,而不是在main()中使用报告。一个经常有用的约定是让检测到错误的低级别报告它,但是将它失败的信息传回给调用代码。除此之外,这意味着你可以区分打开文件的错误和关闭文件的错误,因为函数知道在检测到错误时尝试做什么,但是调用函数只知道当它出现某种问题时正在处理文件。您可以使用或多或少的详细方案来记录错误并将其报告给调用链。

  

[6]写了if (sscanf(buff, "%d", &(*vet)[i]) != 1)它与if (sscanf(buff, "%d", &(*vet)[i]) == EOF)相同?但while (fgets(buff, sizeof(buff), fin))已完成文件控件的结束?

如果字符串中没有数据({是空字符串),则sscanf()调用将返回EOF。如果有字符,它将返回0,但它们不能被视为数字。如果有一个字符序列形成一个数字(但可能存在尾随垃圾 - 字母或标点符号 - 紧接在有效数字之后它将返回1.再次,请参阅评论以部分讨论如何处理此问题 - 尽管该评论更多的是关于在一条线上处理多个数字。

  

[7]我还没弄清楚为什么if (i + 8 < n_max)。你已经告诉我但我不明白。为什么我不能if (i < n_max)

你可以if (i < n_max)。但是,如果您通过realloc()返回4个字节(因为i + 1 == n_max),系统可能无法对其执行任何有用的操作,因此您无法通过打电话。 OTOH,尝试释放每个字节并没有太大的危害。我的猜测是,如果你分配了几百个或更多的数字,并且在你将值读入最后一个之前文件结束,你通常会释放几百个字节(或更多)的空间,这可能是有用的。这是一个判断电话。我选择复杂化的东西;对不起,我这样做了。

答案 1 :(得分:2)

将(整个)文件读入缓冲区,其大小与文件大小相同。否realloc

关闭您的文件,不要担心以后在程序中泄漏句柄。

扫描所述缓冲区以查找换行符。数数吧。不要担心数字。

现在您知道有多少数字,分配您的数字数组。没有realloc。再次扫描缓冲区时写入该数组。

如果您不再需要它,请释放您的缓冲区。

除非你在谈论到其他进程,套接字或4 GB以上文件的管道,否则用低级语言缓冲人类语言数据是没有意义的。或者,如果您正在编写将在电梯控制器或微波炉或电动剃须刀或其他东西上运行的代码 - 但是您没有任何文件,嗯?如果您的文件适合您的地址空间(现在大多数文件都是这样做的话),那么缓冲是对内存占用的过早优化,这会花费您的时间,包括写入时间和运行时间。