如何正确释放数组而没有错误?

nev*_*ind 6 c memory malloc memory-management segmentation-fault

我在@GWW的帮助下想到了这段代码,但现在我不能freechar**

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

    /*   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;
}
Run Code Online (Sandbox Code Playgroud)

所以,我不明白为什么我free final_filenamesimages_filenames

这段代码给我的错误是:

*** glibc detected *** ./main: double free or corruption (!prev): 0x0986d228 ***
Run Code Online (Sandbox Code Playgroud)

如何正确无误地free排列我的阵列?

Jon*_*ler 11

问题是您正在释放可能已经释放的指针,并且您不知道正在使用多少空间,没有指向最近分配的空间的指针(通常),因此您无法释放准确地记忆。在中main(),您具有:

char **images_filenames;                     

[...]                                

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

[...]

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

[...]

deallocate2D(images_filenames, num_lines);
Run Code Online (Sandbox Code Playgroud)

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

因此,您需要:

if (readInputFile(fpin, &images_filenames) == -1) {
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)

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


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

除...

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

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

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

               if ((file_images = (char**) realloc(file_images, s * sizeof (char*))) == NULL)
Run Code Online (Sandbox Code Playgroud)

但是,您使用的技术存在一些特定的问题。纯代码样式:当一行包含if带有嵌入式分配的,并且行变得太长时,请在条件之前将分配拆分:

                file_images = (char**) realloc(file_images, s * sizeof (char*));
                if (file_images == NULL)
Run Code Online (Sandbox Code Playgroud)

现在只剩下一个细微的错误。如果realloc()失败怎么办...

没错,您已经泄漏了内存,因为in中的file_images值为null,因此无法释放它以前指向的内容。 永远不要写

x = realloc(x, size);
Run Code Online (Sandbox Code Playgroud)

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

                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;
                }
            }
Run Code Online (Sandbox Code Playgroud)

通常,错误消息应打印在stderr; 上。我还没有解决。

请注意,我小心地将的最后一个(非null)值复制回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;              
        }
Run Code Online (Sandbox Code Playgroud)

需要设置此错误返回*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;
            }                                                                   
Run Code Online (Sandbox Code Playgroud)

这个测试很奇怪。您知道那file_images[num_lines]不是null,strncpy()也不会改变它。您不需要测试和错误处理。

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

好...

    //realloc to number of lines in the file, to avoid wasting memory
Run Code Online (Sandbox Code Playgroud)

很好 这几乎不值得;即使在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;
Run Code Online (Sandbox Code Playgroud)

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

    } else {
        file_images = final_filenames;
Run Code Online (Sandbox Code Playgroud)

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

        deallocate2D(final_filenames, num_lines);
Run Code Online (Sandbox Code Playgroud)

等等-您要释放所有精心分配的空间吗?因此,您毕竟不会使用它吗?上面的分配只是在附近复制了一个指针值;它没有复制内存...

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

此注释是错误的-成功返回后,内存已被释放。


随机猜测-您无需使用“ 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;
}
Run Code Online (Sandbox Code Playgroud)