我正在尝试编写一个程序,该程序将从文件中恢复已删除的图像,并将这些图像中的每一个写入各自的单独文件中。我已经在这个问题上停留了几天,并且已经尽力解决了这个问题,但是现在我意识到我需要一些指导。我的代码总是可以很好地编译,但是每次运行程序时,我都会遇到分段错误。使用valgrind告诉我没有任何内存泄漏。
尽管我不确定如何解决问题,但我认为我已查明了问题所在。当我通过调试器运行程序时,它总是停留在最后一个“ else”条件下的代码处(在该条件下,注释为“如果已找到JPEG”),并显示有关分段错误的错误消息。
我已尝试在此代码行的顶部打开并初始化文件指针jpegn,以防止在运行此条件时jpegn变为NULL,但这不能解决该错误。
我对编程(以及本站点)非常陌生,因此任何建议或建议都将有所帮助。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef uint8_t BYTE;
int main(int argc, char *argv[])
{
if(argc!=2) // Checks if the user typed in exactly 1 command-line argument
{
printf("Usage: ./recover image\n");
return 1;
}
if(fopen(argv[1],"r") == NULL) // Checks if the image can be opened for reading
{
printf("This image cannot be opened for reading\n");
return 1;
}
FILE *forensic_image = fopen(argv[1],"r"); // Opens the image inputted and stores it in a new file
BYTE *buffer = malloc(512 * sizeof(BYTE)); // Dynamically creates an array capable of holding 512 bytes of data
if(malloc(512*sizeof(BYTE)) == NULL) // Checks if there is enough memory in the system
{
printf("System error\n");
return 1;
}
// Creates a counting variable, a string and two file pointers
int JPEG_num=0;
char *filename = NULL;
FILE *jpeg0 = NULL;
FILE *jpegn = NULL;
while(!feof(forensic_image)) // Repeat until end of image
{
fread(buffer, sizeof(BYTE), 512, forensic_image); // Read 512 bytes of data from the image into a buffer
// Check for the start of a new JPEG file
if(buffer[0] == 0xff & buffer[1] == 0xd8 & buffer[2] == 0xff & (buffer[3] & 0xf0) == 0xe0)
{
// If first JPEG
if(JPEG_num == 0)
{
sprintf(filename, "%03i.jpg", JPEG_num);
jpeg0 = fopen(filename, "w");
fwrite(buffer, sizeof(BYTE), 512, jpeg0);
}
else // If not first JPEG
{
fclose(jpeg0);
JPEG_num++;
sprintf(filename, "%03i.jpg", JPEG_num);
jpegn = fopen(filename, "w");
fwrite(buffer, sizeof(BYTE), 512, jpegn);
}
}
else // If already found JPEG
{
fwrite(buffer, sizeof(BYTE), 512, jpegn);
}
}
// Close remaining files and free dynamically allocated memory
fclose(jpegn);
free(buffer);
}
答案 0 :(得分:2)
您的代码中有很多问题。如果valgrind没有发现这些,我会感到惊讶。
首先是这个
if(fopen(argv[1],"r") == NULL) // Checks if the image can be opened for reading
{
printf("This image cannot be opened for reading\n");
return 1;
}
FILE *forensic_image = fopen(argv[1],"r"); // Opens the image inputted and stores it in a new file
这不是致命的,但是您两次打开了相同的文件并丢弃了第一个文件指针。但是,下面类似的模式肯定是内存泄漏:
BYTE *buffer = malloc(512 * sizeof(BYTE)); // Dynamically creates an array capable of holding 512 bytes of data
if(malloc(512*sizeof(BYTE)) == NULL) // Checks if there is enough memory in the system
{
printf("System error\n");
return 1;
}
在这里您分配了512个字节两次,并且只将第一个分配保留在指针buffer
中,而第二个分配丢失了。
然后这个:
char *filename = NULL;
// ...
sprintf(filename, "%03i.jpg", JPEG_num);
您正在将字符串写入未分配的内存!
以及以下行:
else // If already found JPEG
{
fwrite(buffer, sizeof(BYTE), 512, jpegn);
}
如何保证jpegn
是有效的文件指针?可能永远不会,因为我在您的代码中看到JPEG_num
始终为0。用else
标记的// If not first JPEG
的部分是无效代码。
答案 1 :(得分:0)
在编译时,始终启用警告,然后修复这些警告。
gcc -ggdb3 -Wall -Wextra -Wconversion -pedantic -std=gnu11 -c "untitled1.c" -o "untitled1.o"
会产生如下警告:
untitled1.c:46:91: warning: suggest parentheses around comparison in operand of ‘&’ [-Wparentheses]
if(buffer[0] == 0xff & buffer[1] == 0xd8 & buffer[2] == 0xff & (buffer[3] & 0xf0) == 0xe0)
注意:单个&
有点与。您真的想要除此语句中的最后一个语句外的所有逻辑和键&&
关于;
FILE *forensic_image = fopen(argv[1],"r");
始终检查(!= NULL)返回值以确保操作成功。如果未成功(== NULL),请致电
perror( "fopen failed" );
将错误消息和系统认为发生错误的文本原因输出到stderr
。
关于:
while(!feof(forensic_image))
请阅读:why while( !feof() is always wrong
关于:
FILE *forensic_image = fopen(argv[1],"r");
这已在前面的代码块中完成。绝对没有必要再次执行此操作,这将在代码中产生问题。建议:替换:
if(fopen(argv[1],"r") == NULL)
{
printf("This image cannot be opened for reading\n");
return 1;
}
具有:
if( (forensic_image = fopen(argv[1],"r") ) == NULL)
{
perror( "fopen for input file failed" );
exit( EXIT_FAILURE );
}
关于:
BYTE *buffer = malloc( 512 * sizeof(BYTE) );
及更高版本:
free( buffer );
这浪费了代码和资源。该项目仅需要一个这样的实例。建议:
#define RECORD_LEN 512
和
unsigned char buffer[ RECORD_LEN ];
关于;
fread(buffer, sizeof(BYTE), 512, forensic_image);
函数:fread()
返回一个size_t
。您应该将返回的值分配给size_t
变量,并检查该值以确保操作成功。实际上,该语句应处于while()
条件下
关于;
sprintf(filename, "%03i.jpg", JPEG_num);
这将导致不确定的行为,并可能导致段错误事件,因为指针filename
已初始化为NULL。建议:
char filename[20];
避免该问题
关于:
else // If not first JPEG
{
fclose(jpeg0);
如果您(例如)使用第3个文件,则jpeg0
已经关闭,导致运行时错误。建议删除该语句:
FILE *jpeg0;
并始终使用jpegn
关于;
else // If already found JPEG
{
fwrite(buffer, sizeof(BYTE), 512, jpegn);
}
在第一个输出文件上未设置 jpegn
,因此会导致崩溃。同样,仅对所有输出文件操作使用jpegn
。
关于:
fwrite(buffer, sizeof(BYTE), 512, jpegn);
这将返回实际写入的(第二个参数)数量的数量,因此应为:
if( fwrite(buffer, sizeof(BYTE), 512, jpegn) != 512 ) { // handle error }
发布的代码包含一些“魔术”数字,例如512。“魔术”数字是没有基础的数字。 “魔术”数字使代码更难以理解,调试等。建议使用enum
语句或#define
语句为这些“魔术”数字赋予有意义的名称,然后在整个过程中使用这些有意义的名称。代码。