分配和释放动态内存

Allocating and freeing dynamic memory

我编写了一个小程序,可以从存储卡中读取 JPEG 并将 JPEG 作为单独的文件写入输出。该程序按我的预期运行,但我知道我的代码写得不好,因为它没有从堆中释放动态内存。

我试图在我的代码中使用 fclose()free(),但是当我这样做时出现以下错误:

free(): double free detected in tcache 2
Aborted

我已经注释掉了这些函数,并且我的代码可以正常工作。 我想知道为什么当我尝试使用这两个功能时它不起作用。我做错了什么?

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

// define/declare variables
typedef uint8_t BYTE;
FILE *outptr; 

int main(int argc, char *argv[])
{
// declare variables
int counter = 0;

// ensure correct command-line useage
if (argc != 2)
{
    printf("Useage: ./recover memorycard\n");
    return 1;
}

// open memory card to read
FILE *inptr = fopen(argv[1], "r");
if (inptr == NULL)                    // ensure enough memory
{
    fprintf(stderr, "Could not open file");
    return 2;
}

// read into infile, 512 bytes at a time until end of file
BYTE buffer[512];
while (fread(buffer, sizeof(BYTE), 512, inptr) == 512)
{
    // if JPEG headerfile is found
    if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0xe0)
    {
        if (counter > 0)             // if not first JPEG
        {
            // close previous file
     //     fclose(outptr);                            // here is the issue
     //     free(outptr);                              // and here
        }

        // allocate memory for new file
        char *outfile = malloc(sizeof(BYTE) * 512);
        if (outfile == NULL)
        {
            printf("Not enough memory for output file\n");
            fclose(inptr);
            return 3;
        }

        // write new file name
        sprintf(outfile, "%03i.jpg", counter);

        // open new file
        outptr = fopen(outfile, "w");
        if (outptr == NULL)
        {
            fprintf(stderr, "Could not open file.\n");
            fclose(inptr);
            return 4;
        }

        // write 512 bytes from infile into new file
        fwrite (buffer, sizeof(BYTE), 512, outptr);

        counter++;

    }

    // if not start of new JPEG
    else if (buffer[0] != 0xff || buffer[1] != 0xd8 || buffer[2] != 0xff || (buffer[3] & 0xf0) != 0xe0)
    {
        // if already found JPEG
        if (counter > 0)
        {
            // keep writing into JPEG
            fwrite (buffer, sizeof(BYTE), 512, outptr);
        }

        continue;
    }

}
// Close remaining files once gone through memory card
//    fclose(inptr);                        // here
//    free(inptr);                          // and here
}

在循环的第一次迭代中,outptr 变量未初始化,因此对其采取的任何操作都会带来未定义的行为。
具体来说,您还没有为它分配从 fopen() 获得的任何值,因此您不应该 fclose() 它。
此外,您还没有为它分配从 malloc()calloc() 获得的任何值,因此您不应该 free() 它。

您需要 fopen() 一个文件,当您需要写入它时,只要需要它(您写入它)就保留指向它的指针,并在完成时 fclose() 。文件处理不需要 malloc()free()

下面是稍微简化的代码:

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

// define/declare variables
typedef uint8_t BYTE;
FILE *outptr = NULL; 

int main(int argc, char *argv[])
{
    // declare variables
    int counter = 0;

    // ensure correct command-line useage
    if (argc != 2)
    {
        printf("Useage: ./recover memorycard\n");
        return 1;
    }

    // open memory card to read
    FILE *inptr = fopen(argv[1], "r");
    if (inptr == NULL)                    // ensure enough memory
    {
        fprintf(stderr, "Could not open file");
        return 2;
    }

    // read into infile, 512 bytes at a time until end of file
    BYTE buffer[512];
    while (fread(buffer, sizeof(BYTE), 512, inptr) == 512)
    {
        // if JPEG headerfile is found
        if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0xe0)
        {
            if (outptr != NULL)             // previous file is opened
            {
                fclose(outptr);             // close it
            }

            char outfilename[ 512];         // output file name

            sprintf(outfilename, "%03i.jpg", counter ++);

            // open new file
            outptr = fopen(outfilename, "w");
            if (outptr == NULL)
            {
                fprintf(stderr, "Could not open file.\n");
                fclose(inptr);
                return 4;
            }
        }

        // whether it's a new or still the same file...
        if(outptr != NULL)
        {
            // keep writing into JPEG
            fwrite (buffer, sizeof(BYTE), 512, outptr);
        }
    }

    fclose(outptr);         // close the last file created
    fclose(inptr);          // as well as the input
}

我已经删除了对 malloc 的文件名调用 - 它足够短,可以自动分配到堆栈上,并且您不需要在使用后 free 它(您忘记了顺便说一句,因此会造成内存泄漏...)

我注意到你在 ifelse 分支中做的 write 完全一样,所以我把它移到了条件之外。

我还删除了 continue; 指令,因为一旦控制到达右大括号,循环就会继续。

最重要的是:我添加了初始化 FILE* outptr = NULL;,因此它可以检查 文件指针 变量是否文件已成功打开,而不是检查一个明显的未连接 counter 变量。

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

// define byte
typedef uint8_t BYTE;


int main(int argc, char *argv[])
{
    // ensure correct command-line useage
    if (argc != 2)
    {
        printf("Useage: ./recover memorycard\n");
        return 1;
    }

    // open memory card 
    FILE *inptr = fopen(argv[1], "r");
    if (inptr == NULL)                    
    {
        fprintf(stderr, "Could not open file");
        return 2;
    }

    // buffer for file names
    char output[8];
    
    // keep track of file number for file names
    int counter = 0; 
    
    // pointer to write JPEGs to
    FILE *outptr = NULL;
    
    // read into infile, 512 bytes at a time until end of file
    BYTE buffer[512];
    while (fread(buffer, sizeof(BYTE), 512, inptr) == 512)
    {
        // New JPEG signatiture found
        if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0xe0)
        {
            // if not first JPEG, close previous output file
            if (outptr != NULL)
                fclose(outptr);
            
            // write new file name
            sprintf(output, "%03i.jpg", counter);
            
            // open new file
            outptr = fopen(output, "w");
            if (outptr == NULL)
            {
                fprintf(stderr, "Could not open file.\n");
                fclose(inptr);
                return 4;
            }
            
            // increment file counter
            counter++;
        }

        // if output file is already open
        if (outptr != NULL)
        {
            // keep writing into JPEG
            fwrite (buffer, sizeof(BYTE), 512, outptr);
        }
    }
    
    // close final file if still open
    if (outptr != NULL)
        fclose(inptr);
}