sprintf 和 fwrite() 的分段错误;程序仅适用于 valgrind

Segmentation Fault with sprintf and fwrite(); program only works with valgrind

我在 运行 运行我的程序时遇到分段错误,该程序只允许我打印出 1 个损坏的图像。但是,当我 运行 使用 valgrind 的程序时,它打印出 50 张照片中的 49 张照片,唯一不工作的照片是最后一张。

在 运行对程序进行 help50 valgrind 后,我意识到 sprintf 存在分段错误,并且在程序末尾的 fwrite() 可能存在问题。如有任何澄清,我们将不胜感激。

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

int main(int argc, char *argv[])
{
    // Check you're actually recovering a file.
    if (argc != 2)
    {
        printf("Invalid entry.\n");
        return 0;
    }

    // Open file for reading.
    FILE *file = fopen(argv[1], "r");
    if (!file)
    {
        return 1;
    }

    // Set 1st photo as jpg.001.
    int counter = 1;

    // Check if bytes are jpg. signatures.
    for (int n = 0; counter < 51; n = n + 512)
    {
        // Sets an array to contain 4 values and directs pointer to start from nth value each time.
        unsigned char array[512];
        fseek(file, n, SEEK_SET);
        fread(array, 1, 512, file); // if EOF, won't have 512 to write into!!!

        // Declare character array jpg.
        char* jpg_name;

        // While 1st 4 bytes are jpg signature.
        if (array[0] == 0xff && array[1] == 0xd8 && array[2] == 0xff && (array[3] & 0xf0) == 0xe0)
        {
            // Convert integer to string and store into jpg character array. Increment image number.
            jpg_name = malloc(4*sizeof(char));
            sprintf(jpg_name, "%03i.jpg", counter);
            counter++;

            // Open images file to write into,allocate memory to jpg file to write into, write 512 bytes from array into image file.
            FILE *images = fopen(jpg_name, "a");
            fwrite(array, 1, 512, images);

            // Free file memory?
            free(jpg_name);
            fclose(images);
        }
        else // If 1st 4 bytes aren't jpg signature.
        {
            // Add bytes to existing image (if present), otherwise repeat loop.
            if (counter > 1)
            {
                FILE *images = fopen(jpg_name, "a");
                fwrite(array, 1, 512, images); // SEGMENTATION FAULT; need to detect end of file.
                fclose(images);
            }
            else
            {
                continue;
            }
        }
    }
}

警告:没有您的输入文件,我无法完全测试这个提议的代码。

警告:提议的代码要求输入文件的第一行包含第一个 .jpg 图像的 'header'

警告:提议的代码要求每张图像都是 512 字节的倍数

建议代码如下:

  1. 干净地编译
  2. 执行所需的功能(就测试而言)
  3. 正确检查 I/O 错误
  4. 以二进制模式正确打开文件(尽管 Linux 不关心)
  5. 假设每个图像都是 512 字节的倍数
  6. 正确区分错误退出和成功退出
  7. 不包含头文件那些内容没有用到

现在,建议的代码:

#include <stdio.h>
#include <stdlib.h>
// #include <stdbool.h>

int main(int argc, char *argv[])
{
    // Check the user entered a command line parameter for a input file name
    if (argc != 2)
    {
        fprintf( stderr, "USAGE: %s inputFileName\n", argv[0] );
        exit( EXIT_FAILURE );
    }

    // Open file for reading.
    FILE *file = fopen(argv[1], "rb");
    if (!file)
    {
        perror( "fopen for input file failed");
        exit( EXIT_FAILURE );
    }

    // Set 1st photo as jpg.001.
    // Declare character array jpg.
    char jpg_name[10];
    FILE *images = NULL;
    
    for (int counter = 0; counter < 51;  )
    {
        unsigned char array[512];
        ssize_t bytesRead;
        if( (bytesRead = fread(array, 1, 512, file) != 512 ) )
        {
            perror( "fread failed" );
            fclose( images );
            fclose( file );
            exit( EXIT_FAILURE );
        }

        // if 1st 4 bytes are jpg signature.
        if (    array[0] == 0xff 
            &&  array[1] == 0xd8 
            &&  array[2] == 0xff 
            && (array[3] & 0xf0) == 0xe0)
        {
            if( images )
            {
                fclose( images );
                images = NULL;
                counter++;
            }
            
            sprintf(jpg_name, "%03i.jpg", counter+1);

            images = fopen(jpg_name, "wb");
            if( ! images )
            {
                perror( "fopen for output file failed" );
                fclose( file );
                exit( EXIT_FAILURE );
            }
        }
        
        // Add bytes to current output image
        if( fwrite(array, 1, 512, images) != 512 )
        {
            perror( "fwrite failed" );
            fclose( images );
            fclose( file );
            exit( EXIT_FAILURE );
        }
    }
    
    fclose( images );
    fclose( file );
}