重新分配后释放2d数组的问题

时间:2010-12-05 20:56:55

标签: c memory memory-management malloc segmentation-fault

我在GWW(用户)的帮助下想出了这段代码,现在我无法释放char **。

这是我的代码(它只是读取输入文件并在屏幕上打印其中的名称):

编辑:

    /*   deallocate2D
corresponding function to dynamically deallocate 2-dimensional array using
 * malloc.
 * accepts a char** as the "array" to be allocated, and the number of rows.
 * as with all dynamic memory allocation, failure to free malloc'ed memory
 * will result in memory leaks
 */
void deallocate2D(char** array, int nrows) {

    /*  deallocate each row  */
    int i;
    for (i = 0; i < nrows; i++) {
        free(array[i]);
    }

    /*  deallocate array of pointers  */
    free(array);
}

int readInputFile(FILE *fp, char **file_images) {
    num_lines = 0;
    int s = 10;
    char line[MAX_LENGTH];
    char **final_filenames;

    while (fgets(line, sizeof line, fp) != NULL) /* read a line */ {
        if (line[0] != '\n') {
            if (num_lines >= s) {
                s += 100;
                if ((file_images = (char**) realloc(file_images, s * sizeof (char*))) == NULL) {
                    printf("Error reallocating space for 2d array: %s\n", strerror(errno));
                    return -1;
                }
            }
            if ((file_images[num_lines] = malloc(MAX_LENGTH * sizeof (char))) == NULL) {
                printf("Error allocating space for 2d array: %s\n", strerror(errno));
                return -1;
            }

            strncpy(file_images[num_lines], line, MAX_LENGTH);
            if (file_images[num_lines] == NULL) {
                printf("Strncpy failed: %s\n", strerror(errno));
                return -1;
            }
            printf("name of file %d is: %s \n", num_lines, file_images[num_lines]);
            num_lines++;
        }
    }
    printf("Num_lines: %d\n",num_lines);
    //realloc to number of lines in the file, to avoid wasting memory
    if ((final_filenames = realloc(file_images, num_lines * sizeof (char*))) == NULL) {
        printf("Error reallocating space for 2d array: %s\n", strerror(errno));
        return -1;
    } else {
        file_images = final_filenames;
        deallocate2D(final_filenames, num_lines);
    }
    return 0;
    //don't forget to free lines 2d array! (here or at the end of the code)
}

int main(int argc, char *argv[]) {
    //pixel* image;
    char **images_filenames;

    //check parameters
    if (argc < 4) {
        printf("Incorrect usage.\nPlease use \"./invert input_filename.ppm charWidth charHeight \"\n");
        return -1;
    }

    printf("Opening input file [%s]\n", argv[1]);
    FILE *fpin = fopen(argv[1], "r");
    if (fpin == NULL) {
        printf("Could not open input file\n");
        return -1;
    }
    if ((images_filenames = ((char**) malloc(10 * sizeof (char*)))) == NULL) {
        printf("Error allocating initial space for 2d array: %s\n", strerror(errno));
        return -1;
    }

    if (readInputFile(fpin, images_filenames) == -1) {
        printf("Error reading image filenames from input\n");
        return -1;
    }

    fclose(fpin);
    printf("###########\n");

    deallocate2D(images_filenames, num_lines);

    printf("Done!\n");
    return 0;
}

所以,我不明白为什么我不能释放final_filenames数组然后释放images_filenames,因为它让我 * glibc检测到 ./ main:double free或corruption(!prev):0x0986d228 * *。

无论如何,如果您在此代码中看到不正确的内容,虽然它有效,但请随时指出。

1 个答案:

答案 0 :(得分:3)

问题是你释放了一个可能已经被释放的指针,并且你不知道有多少空间正在使用没有指向最近分配空间的指针(一般情况下)所以你不能准确地释放内存。在main()中,您有:

char **images_filenames;                     

[...]                                

if ((images_filenames = ((char**) malloc(10 * sizeof (char*)))) == NULL) {

[...]

if (readInputFile(fpin, images_filenames) == -1) {

[...]

deallocate2D(images_filenames, num_lines);

您分配10个字符指针,然后将该数组传递给readInputFile()函数。在该函数内部,有重新分配数组的代码,但是您没有为主程序提供一种知道新地址是什么的方法。你这样做的方法是通过将指针传递给你想要修改的任何东西,或者让函数返回修改后的值(或者你采用肮脏的做法,比如使用全局变量而不是参数 - 但是你不应该这样做)。

所以,你需要:

if (readInputFile(fpin, &images_filenames) == -1) {

readInputFile()函数中,您需要进行大量更改 - 处理三指针参数的最大变化,然后是各种编码问题:

int readInputFile(FILE *fp, char ***ppp_files)
{
    num_lines = 0;
    int s = 10;
    char line[MAX_LENGTH];
    char **file_images = *ppp_files;
    char **final_filenames;

更新:我没有注意到这只是初始化num_lines,而不是声明它。因此,num_lines必须是某种全局变量......下面的一些评论需要调整以允许这种情况。


到目前为止,这种变化(几乎)是微不足道的;我们得到一个指向'char **'的指针,因此是三指针参数。要简化以下代码,请在旧名称(file_images)下创建参数的本地副本,并使用参数指向的值对其进行初始化。以下代码可以继续使用file_images;只需确保在返回之前更新参数。

...除

你假设's = 10',但实际上,你应该让main函数告诉你有多少行可用。它确实分配了10行,但如果没有经过仔细审查就不清楚了。您应该让main()程序说明预分配了多少行 - 该函数的额外参数。您还面临这样的问题:main()程序无法告诉deallocate2D()函数数组中有多少行,因为它不知道。目前尚不清楚你的代码如何编译;你在这里有一个局部变量num_lines,但是在num_lines中有一个变量main()的引用没有声明。局部变量屏蔽任何全局变量。

    while (fgets(line, sizeof line, fp) != NULL) {
        if (line[0] != '\n') {
            if (num_lines >= s) {
                s += 100;

添加大量行是个好主意;它“分摊”重新分配的成本。

               if ((file_images = (char**) realloc(file_images, s * sizeof (char*))) == NULL)

您使用的技术存在特定问题。 纯代码样式:当一行包含带有嵌入赋值的if并且它太长时,在条件之前拆分该赋值:

                file_images = (char**) realloc(file_images, s * sizeof (char*));
                if (file_images == NULL)

现在只剩下一个微妙的错误。如果realloc()失败,会发生什么......

没错,你已经泄露了内存,因为file_images中的值为null,所以无法释放它曾经指向的内容。 从不写

x = realloc(x, size);

故障时泄漏内存!因此,您需要:

                char **new_space = realloc(file_images, s * sizeof (char*));
                if (new_space == NULL)
                {
                    printf("Error reallocating space for 2d array: %s\n",
                           strerror(errno));
                    *ppp_files = file_images;
                    return -1;
                }
            }

作为一般规则,错误消息应打印在stderr;我没有解决这个问题。

请注意,我小心地将file_images的最后一个(非空)值复制回主程序中的变量。也可能适合对大小进行相同的操作(另一个接口更改),或者使用结构来封装数组 - 大小和指向其基础的指针。

        if ((file_images[num_lines] = malloc(MAX_LENGTH * sizeof (char))) == NULL)
        {
             printf("Error allocating space for 2d array: %s\n", strerror(errno));
             return -1;              
        }

此错误返回需要设置*ppp_files = file_images;

            strncpy(file_images[num_lines], line, MAX_LENGTH);


            if (file_images[num_lines] == NULL) {               
                printf("Strncpy failed: %s\n", strerror(errno));
                return -1;
            }                                                                   

这个测试很奇怪;您知道file_images[num_lines]不为空,strncpy()不会更改。您不需要测试和错误处理。

            printf("name of file %d is: %s \n", num_lines, file_images[num_lines]);
            num_lines++;
        }                                                              
    }
    printf("Num_lines: %d\n",num_lines);

行...

    //realloc to number of lines in the file, to avoid wasting memory

很好的触感。它几乎不值得;即使在64位机器上,你最多也浪费不到1 KiB。但是,整洁没有坏处 - 好。

    if ((final_filenames = realloc(file_images, num_lines * sizeof (char*))) == NULL) {
        printf("Error reallocating space for 2d array: %s\n", strerror(errno));
        return -1;

同样,您需要在返回前设置*ppp_files = file_images;

    } else {
        file_images = final_filenames;

这不会影响main()程序中的值。它需要再次*ppp_files = file_images;

        deallocate2D(final_filenames, num_lines);

坚持 - 你释放所有精心分配的空间?所以你毕竟不打算使用它?上面的赋值只复制了一个指针值;它没有复制记忆......

    }
    return 0;
    //don't forget to free lines 2d array! (here or at the end of the code)
}

此评论错误 - 成功返回后,内存已被解除分配。


Lemme guess - 你不要使用'vim'或其他'vi'衍生物进行编辑。在第1列中确实具有其功能的左大括号的人,因为这样您就可以使用“]]”或“[[”在文件中向前或向后跳转到下一个或上一个函数的开头。使用代码无法正常工作。


嗯,这是一个开始诊断......这是使用结构来传递文件名数组的工作代码。我使用从结构中复制的局部变量离开了readInputFile()函数的主体,并确保结构始终正确更新。

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

enum { MAX_LENGTH = 512 };

typedef struct FileNameArray
{
    size_t   nfiles;    /* Number of file names allocated and in use */
    size_t   maxfiles;  /* Number of entries allocated in array */
    char   **files;     /* Array of file names */
} FileNameArray;

static void deallocate2D(FileNameArray *names)
{
    for (size_t i = 0; i < names->nfiles; i++)
        free(names->files[i]);
    free(names->files);
    names->nfiles   = 0;
    names->files    = 0;
    names->maxfiles = 0;
}

static int readInputFile(FILE *fp, FileNameArray *names)
{
    int    num_lines  = names->nfiles;
    int    max_lines  = names->maxfiles;
    char **file_names = names->files;
    char line[MAX_LENGTH];
    char **final_filenames;

    while (fgets(line, sizeof line, fp) != NULL)
    {
        if (line[0] != '\n')
        {
            /* Remove newline from end of file name */
            char *nl = strchr(line, '\n');
            if (nl != 0)
                *nl = '\0';
            if (num_lines >= max_lines)
            {
                max_lines += 100;
                char **space = realloc(file_names, max_lines * sizeof (char*));
                if (space == NULL)
                {
                    fprintf(stderr, "Error reallocating space for 2d array: %s\n",
                            strerror(errno));
                    return -1;
                }
                names->maxfiles = max_lines;
                names->files = space;
                file_names = space;
            }
            if ((file_names[num_lines] = malloc(strlen(line) + 1)) == NULL)
            {
                fprintf(stderr, "Error allocating space for 2d array: %s\n",
                        strerror(errno));
                return -1;
            }
            names->nfiles++;
            strcpy(file_names[num_lines], line);
            printf("name of file %d is: %s \n", num_lines, file_names[num_lines]);
            num_lines++;
        }
    }

    printf("Num_lines: %d\n", num_lines);
    //realloc to number of lines in the file, to avoid wasting memory
    if ((final_filenames = realloc(file_names, num_lines * sizeof (char*))) == NULL)
    {
        fprintf(stderr, "Error reallocating space for 2d array: %s\n",
                strerror(errno));
        return -1;
    }
    names->maxfiles = num_lines;
    names->files    = final_filenames;
    return 0;
}

int main(int argc, char *argv[])
{
    FileNameArray names = { 0, 0, 0 };

    //check parameters
    if (argc < 4)
    {
        fprintf(stderr, "Usage: %s input_filename.ppm charWidth charHeight\n",
                argv[0]);
        return -1;
    }

    printf("Opening input file [%s]\n", argv[1]);
    FILE *fpin = fopen(argv[1], "r");
    if (fpin == NULL) {
        fprintf(stderr, "Could not open input file %s (%s)\n",
                argv[1], strerror(errno));
        return -1;
    }

    if ((names.files = malloc(10 * sizeof (char*))) == NULL)
    {
        fprintf(stderr, "Error allocating initial space for 2d array: %s\n",
                strerror(errno));
        return -1;
    }
    names.maxfiles = 10;

    if (readInputFile(fpin, &names) == -1)
    {
        fprintf(stderr, "Error reading image filenames from input\n");
        return -1;
    }

    fclose(fpin);
    printf("###########\n");

    deallocate2D(&names);

    printf("Done!\n");
    return 0;
}