我的 recover.c 代码可以正确恢复 jpeg,但没有通过 cs50 检查

My code for recover.c recovers jpegs correctly but does not pass cs50 check

这是代码。我是 C 语言的初学者,因此非常感谢任何缩短我的代码的建议。我检查了所有 50 张图像,它们看起来很完美,但代码没有通过 cs50 检查。

int main(void)
{
    FILE* source = fopen("card.raw", "r");



    uint8_t jpg[512];
    int direct = 0;
    int jpgcounter = 0;
    uint8_t checkjpg[4];
    FILE* outputfile ;
    char filename[7] ;

    while(feof(source) == 0)
    {
        fread(jpg,512,1,source);

        for (int i = 0 ; i < 4 ; i++)
        {
            checkjpg[i] = jpg[i];
        }

        if( checkjpg[0] == 0xff && checkjpg[1] == 0xd8 && checkjpg[2] == 0xff && checkjpg[3] >= 0xe0 && checkjpg[3] <= 0xef )
        {

            if ( direct == 0 )
            {
              sprintf( filename, "%03d.jpg" , jpgcounter);
              outputfile = fopen(filename, "w");
              fwrite(jpg,512,1,outputfile);
              direct = 1;
            }
            else
            {
                fclose(outputfile);
                jpgcounter++;
                sprintf( filename, "%03d.jpg" , jpgcounter);
              outputfile = fopen(filename, "w");
              fwrite(jpg,512,1,outputfile);
            }

        }
        else
        {
           fwrite(jpg,512,1,outputfile) ;
        }
    }
    fclose(outputfile);
    fclose(source);
    return 0;
}

现在的主要问题是它没有通过 cs50 检查,所以一定有一些我在 card.raw 上遗漏的数据或其他东西,人眼无法检测到图像中的这些错误,但计算机可以.

我想我知道问题出在哪里了。您永远不会初始化 outputfile。作为初学者,您应该始终在声明变量时对其进行初始化。从不写

int i;

int i = ...;

并给它一个初始值(0-1INT_MAX,随你喜欢)。这对于指针来说非常重要。如果你写

FILE * outputfile;

那个指针指向哪里?嗯,这是随机的。它可能指向任何地方或任何地方。这是一个 "invalid" 指针,在任何情况下您现在都不能使用 outputfile 因为任何用法的结果都是未定义的。但是你怎么知道它已经初始化了呢?好吧,你不能!你不能,因为你从来没有给它分配任何你可以检查的值。

更好的代码是

FILE * outputfile = NULL;

因为现在 outputfile 有一个定义值,它是 NULL 这意味着你可以测试它是否被初始化

if (outputfile == NULL) {
    // Not initialized
} else {
    // Initialized
}

查看您的代码并思考以下问题:
如果您的循环第一次运行并且第一个 512 字节块与 0xffd8ff.. 的 if 测试不匹配,会发生什么情况?然后你以 else-case 结束,这个 case 执行以下操作

fwrite(jpg,512,1,outputfile) ;

但是 outputfile 这里有什么价值?没有价值,它是完全未定义的。您在任何地址访问内存,这很可能会使您的应用程序崩溃,这正是您所发生的情况。如果您将 outputfile 初始化为 NULL,则正确的代码应该是:

} else if (outputfile != NULL) {
    fwrite(jpg,512,1,outputfile);
}

这是一个超级美化、清理过的代码版本。代码未经测试,我只知道它可以编译。我知道它也变大了很多,但请考虑还有大量的评论,代码正在检查和处理所有预期的错误,甚至打印到 STDERR 出了什么问题。如果我愿意,我可以轻松压缩它 down to 58 lines,这只比问题中的代码多 7 行,但你的代码没有捕获或打印所有这些错误:

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

int main ( void ) {
    FILE * inputFile = fopen("card.raw", "r");
    if (!inputFile) { // Same as "if (inputFile == NULL)"
        fprintf(stderr, "Cannot open input file!\n");
        // Don't close it, it didn't open!
        return 1;
    }

    // Always declare variables in the smallest possible scope!
    // Don't declare anything here you only need in the loop and
    // whose value doesn't need to survive a loop iteration.
    int fileCounter = 0;
    FILE * outputFile = NULL;
    bool writeError =  false;

    for (;;) { // Endless loop, will never terminate on its own
        uint8_t cluster[512];
        // It will read one cluster or nothing at all.
        if (fread(cluster, sizeof(cluster), 1, inputFile) != 1) {
            // If we have an open output file, close it.
            if (outputFile) {
                fclose(outputFile);
                outputFile = NULL; // Not required but good style.
            }
            break; // Terminates the loop!
            // Not reached, code flow continues after the loop.
        }

        // Check if start of new _or first_ JPG file.
        if (cluster[0] == 0xFF && cluster[1] == 0xd8
            && cluster[2] == 0xFF && cluster[3] >= 0xE0 && cluster[3] <= 0xEF
        ) {
            char filename[8];
            snprintf(filename, sizeof(filename), "%03d.jpg", fileCounter++);

            // Start nof an new JPG file.
            // If we have an "old" one, time to close it
            if (outputFile) {
                fclose(outputFile);
            }
            // Open new file
            outputFile = fopen(filename, "w");
            if (!outputFile) {
                // Cannot create output file.
                writeError = true;
                break; // Terminates the loop!
                // Not reached, code flow continues after the loop.
            }
        }

        // If we have an output file, write the cluster to it.
        if (outputFile) {
            if (fwrite(cluster, sizeof(cluster), 1, outputFile) != 1) {
                // Write error.
                writeError = true;
                // Close the file.
                fclose(outputFile);
                break; // Terminates the loop!
                // Not reached, code flow continues after the loop.
            }
        }
    }

    // If we end up here, we ran into one of the "breaks"
    // and now need to find out which one.
    bool exitWithError = false;
    if (writeError) {
        exitWithError = true;
        fprintf(stderr, "Counldn't create/write to output file!\n");
    } else if (ferror(inputFile) != 0) {
        exitWithError = true;
        fprintf(stderr, "Counldn't read from input file file!\n");
    }
    // Otherwise input file was just at the end.

    // Final clanup:
    fclose(inputFile);

    return (exitWithError ? 1 : 0);
}

我与您分享这段代码,因为学习某些编码概念可能最简单,只需看看其他人是如何编写代码的。