JPG 恢复程序中的分段错误(我假设是指针/数组问题)C

问题描述 投票:0回答:1

用 C 语言编写一个程序,从存储卡的原始数据中恢复“丢失”的 JPG。我对我的整体代码和逻辑非常有信心,如果我错了,请纠正我,但我在运行时遇到分段错误。

我假设问题出在我的缓冲区数组中。我的目标是制作一个长度为512的int8_t(一个字节)类型的缓冲区数组,然后一次又一次地从存储卡中重复读取一个块到其中,在写入新块时删除前一个块。我想也许不是替换旧块,而是添加到旧块上并超过我的 512 数组。这是我的代码,有什么想法吗?

我也尝试过创建另一个缓冲区作为指针,并向其分配 512 字节的数据,但也没有成功。

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


int main(int argc, char *argv[])
{
    // Ensure proper usage
    if (argc != 2)
    {
        printf("Usage: ./recover <filename>\n");
        return 1;
    }
    // Remember file name and open, checking if it opened correctly
    char *file_name = argv[1];
    FILE *input = fopen(file_name, "r");

    if (input == NULL)
    {
        printf("file <%s> could not be opened\n", file_name);
        return 1;
    }
    // initialize buffer and other variables/types
    typedef uint8_t BYTE;

    int BLOCK_SIZE = 512;

    BYTE buffer[512];

    int JPEG_COUNTER = 0;

    FILE *img = NULL;
    // check if at end of memory card
    while (fread(&buffer, 1, BLOCK_SIZE, input) == BLOCK_SIZE)
    {   // check if a new JPG has started
        if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0)
        {   //check if the first JPG on card, or start of sequential ones, initialize and write into new file from buffer
            if (JPEG_COUNTER < 1)
            {
                fread(&buffer, 1, BLOCK_SIZE, input);
                char *file_name = malloc(17 * sizeof(char));
                if (file_name == NULL)
                {
                    return 1;
                }
                sprintf(file_name, "%.3i.jpg", JPEG_COUNTER);
                img = fopen(file_name, "w");
                fwrite(&buffer, 1, BLOCK_SIZE, img);
                JPEG_COUNTER++;
            }
            else
            {
                fclose(img);
                fread(&buffer, 1, BLOCK_SIZE, input);
                sprintf(file_name, "%.3i.jpg", JPEG_COUNTER);
                img = fopen(file_name, "w");
                fwrite(&buffer, 1, BLOCK_SIZE, img);
                JPEG_COUNTER++;


            }
        }
        // continue writing onto unfinished JPG
        else
        {
            fread(&buffer, 1, BLOCK_SIZE, input);
            fwrite(&buffer, 1, BLOCK_SIZE, img);
        }
    }
    free(file_name);
    return 0;
}
c pointers cs50
1个回答
0
投票

我尝试了你的代码,并遇到了一系列挑战,因为它涉及尝试识别和问题以使程序更紧密地按预期工作。

第一个问题是确定读取文件的第一个块是“jpg”文件。添加一些“printf”语句可以在终端上提供列出的输出,也可以通过调试器查看。

    while (fread(&buffer, 1, BLOCK_SIZE, input) == BLOCK_SIZE)
    {   // check if a new JPG has started
        printf("Here is what is in the start of the buffer: %x  %x  %x  %x\n", buffer[0], buffer[1], buffer[2], buffer[3]);
        printf("buffer[3] & 0xf0 results in a value of %d\n", (buffer[3] & 0xf0));
        if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0)

除了这些附加的“printf”语句之外,“else”块中还添加了另一个“printf”语句,据称该语句将后续数据块打印到输出文件。

        else
        {
            printf("Continue writing blocks after the first block\n");
            fread(&buffer, 1, BLOCK_SIZE, input);
            fwrite(&buffer, 1, BLOCK_SIZE, img);
        }

有了这些额外的信息,立即显示的是,尽管第四个字节是用于测试的“jpg”文件中的实际值,但第一个读取数据块的前四个字节不满足程序的测试条件。

craig@Vera:~/C_Programs/Console/Recover/bin/Release$ ./Recover cat.jpg
Here is what is in the start of the buffer: ff  d8  ff  e0
buffer[3] & 0xf0 results in a value of 224
Continue writing blocks after the first block
Segmentation fault (core dumped)

对第四个字节执行“and”操作没有得到预期的零值,因此不会打开输出文件,随后,当尝试写入该文件时,会发生未定义的行为“write”很可能被推送到 NULL 指针。

因此,重构该测试以仅检查前三个字节确实最初定义了输出文件指针(“img”)并开始将数据写入输出文件。然而,当读写周期完成时,程序使用了“free”函数,这不是正确的操作,导致另一个程序转储。

    printf("Exited the while loop\n");
    free(file_name);
    return 0;

Continue writing blocks after the first block
Here is what is in the start of the buffer: 5  83  8f  ca
buffer[3] & 0xf0 results in a value of 192
Continue writing blocks after the first block
Exited the while loop
munmap_chunk(): invalid pointer
Aborted (core dumped)

“fclose”语句将取代“free”函数。

    printf("Exited the while loop\n");
    fclose(img);
    return 0;

通过这些初始的重构,程序确实运行完成并创建了一个文件。但是,当尝试使用图像查看实用程序查看创建的文件时,该文件将无法正确打开。

Could not load image '000.jpg'

此外,当查看创建的文件的大小属性时,它大约是正在测试的原始输入文件大小的一半。

File size of cat.jpg:   136.6 kb
File size of 000.jpg     68.1 kb

这是因为“while”循环中的每个文件写入实际上都会读取输入文件两次。

完成所有这些后,我退后一步,查看“while”循环中“读取”和“写入”应该发生的位置。以下是代码的重构版本,用于解决许多问题,至少提供一个功能性的文件复制程序。

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

#define BLOCK_SIZE 512

int main(int argc, char *argv[])
{
    // Ensure proper usage
    if (argc != 2)
    {
        printf("Usage: ./recover <filename>\n");
        return 1;
    }
    // Remember file name and open, checking if it opened correctly
    char *file_name = argv[1];
    FILE *input = fopen(file_name, "r");
    int x;      /* Used this to handle any last block size that is not 512 bytes    */

    if (input == NULL)
    {
        printf("file <%s> could not be opened\n", file_name);
        return 1;
    }
    // initialize buffer and other variables/types
    typedef uint8_t BYTE;

    BYTE buffer[BLOCK_SIZE];

    int jpeg_counter = 0;

    FILE *img = NULL;
    // check if at end of memory card
    while (1)                                       /* Simplify while loop logic                */
    {
        x = fread(buffer, 1, BLOCK_SIZE, input);    /* Check if at the end of the memory card   */
        printf("Block size read: %d\n", x);         /* Added for debug information              */
        if (x != BLOCK_SIZE)                        /* Final partial block writing              */
        {
            fwrite(buffer, 1, x, img);
            break;                                  /* Exit the while loop here                 */
        }
        // Check if a new JPG has started
        if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && buffer[3] == 0xe0)   /* ff d8 ff e0  were found as the first bytes of a jpeg file    */
        {
            //check if the first JPG on card, or start of sequential ones, initialize and write into new file from buffer
            if (jpeg_counter < 1)
            {
                printf("jpeg_counter: %d\n", jpeg_counter);     /* Added for debug information  */
                //fread(buffer, 1, BLOCK_SIZE, input);
                char *file_name = malloc(17 * sizeof(char));
                if (file_name == NULL)
                {
                    return 1;
                }
                sprintf(file_name, "%.3i.jpg", jpeg_counter);
                img = fopen(file_name, "w");
                fwrite(buffer, 1, BLOCK_SIZE, img);
                jpeg_counter++;
            }
            else
            {
                printf("jpeg_counter: %d\n", jpeg_counter);     /* Added for debug information  */
                fclose(img);
                //fread(buffer, 1, BLOCK_SIZE, input);
                sprintf(file_name, "%.3i.jpg", jpeg_counter);
                img = fopen(file_name, "w");
                fwrite(buffer, 1, BLOCK_SIZE, img);
                jpeg_counter++;
            }
        }
        // Continue writing onto unfinished JPG
        else
        {
            printf("Reading and writing file\n");               /* Added for debug information  */
            fwrite(buffer, 1, BLOCK_SIZE, img);
        }
    }
    fclose(input);  /* Close the files  */
    fclose(img);
    return 0;
}

我很抱歉将整个程序包括在内,因为重构涵盖了整个程序。以下是重点。

  • 正如前面的好评论中所指出的,变量通常是小写的,并且诸如“BUFFER_SIZE”之类的值被定义为此处完成的常量。
  • 当使用诸如“buffer”之类的字符数组时,名称本身就是指向数组的指针,因此不应在“fread”和“fwrite”函数中使用&符号。
  • 为了纠正“while”循环期间发生的双重读取,简化了“while”循环,将每个数据块的读取移至循环的开头,并在读取最后一个数据块时,然后使用“break”语句退出循环以处理小于预期 512 字节块的数据块。

结果是执行程序生成了用于测试的“jpg”文件的副本。文件大小匹配,并且可以使用图像查看器实用程序打开和查看新文件。

照原样,该程序要么复制实际的“jpg”文件,要么在读取其他类型的文件时出现分段错误。因此,仍有改进的空间。希望这能提供一些关于什么有效、什么无效的想法。由此得出的结论可能是深入研究“C”教程文献,因为它涉及文件处理(打开、读取、写入等)、“while”循环处理以及图像文件的字节序列,例如“ jpg”文件。

© www.soinside.com 2019 - 2024. All rights reserved.