为什么这个缓冲区指向了不可寻址的字节?

4

编辑:感谢回复者指出,我已将fread更改为(...sizeof buffer, 1,...),但现在在valgrind中出现了此错误:

==2409== Invalid read of size 4
==2409==    at 0x51AB8D0: fread (iofread.c:41)
==2409==    by 0x4007B6: main (recover2.c:31)
==2409==  Address 0x5502000 is not stack'd, malloc'd or (recently) free'd
==2409== 
==2409== Use of uninitialised value of size 8
==2409==    at 0x51B8787: _IO_sgetn (genops.c:495)
==2409==    by 0x51AB93E: fread (iofread.c:42)
==2409==    by 0x4007B6: main (recover2.c:31)
==2409== 
==2409== Invalid read of size 8
==2409==    at 0x51B8787: _IO_sgetn (genops.c:495)
==2409==    by 0x51AB93E: fread (iofread.c:42)
==2409==    by 0x4007B6: main (recover2.c:31)
==2409==  Address 0x40 is not stack'd, malloc'd or (recently) free'd
==2409== 
==2409== 
==2409== Process terminating with default action of signal 11 (SIGSEGV)
==2409==  Access not within mapped region at address 0x40
==2409==    at 0x51B8787: _IO_sgetn (genops.c:495)
==2409==    by 0x51AB93E: fread (iofread.c:42)
==2409==    by 0x4007B6: main (recover2.c:31)
==2409==  If you believe this happened as a result of a stack
==2409==  overflow in your program's main thread (unlikely but
==2409==  possible), you can try to increase the size of the
==2409==  main thread stack using the --main-stacksize= flag.
==2409==  The main thread stack size used in this run was 8388608.

我是新来的,希望我的翻译能让您理解。我正在编写代码,从文件中检索数据并将其复制到jpeg文件中。该代码旨在通过头文件找到jpg文件,然后将其写入文件。代码如下:

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

int main(int argc, char* argv[])
{

FILE* file = fopen("card.raw", "r");
if (file == NULL)
{ 
    printf("Could not open file!\n");
    return 1;
}

char title[7];
int currentImage = 0;
uint8_t buffer[512];
FILE* img;
while (fread(buffer, sizeof(buffer), 512, file) == 1)
{
printf("found data!\n");
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff)
{
    if (buffer[3] == 0xe0 || buffer[3] == 0xe1 || buffer[3] == 0xe2 || buffer[3] == 0xe3 || buffer[3] == 0xe4 || buffer[3] == 0xe5 || buffer[3] == 0xe6 || buffer[3] == 0xe7 || buffer[3] == 0xe8 || buffer[3] == 0xe9 || buffer[3] == 0xea || buffer[3] == 0xeb || buffer[3] == 0xec || buffer[3] == 0xed || buffer[3] == 0xee || buffer[3] == 0xef)
    {
        printf("Found new jpg!\n");
        sprintf(title, "%03d.jpg", currentImage);
        img = fopen(title, "a");
        currentImage++;
        printf("size of buffer to print is %lu\n", sizeof(buffer));
        fwrite(buffer, sizeof(buffer), 1, img);
        }
}
else if (currentImage > 0)
{ 
        fwrite(buffer, sizeof(buffer), 1, img);


}

}
}

一旦找到jpeg文件并进行fwrite操作后,程序返回while循环时出现了分段错误。

Valgrind报告的错误信息如下:

==1866== Syscall param read(buf) points to unaddressable byte(s)
==1866==    at 0x5228810: __read_nocancel (syscall-template.S:81)
==1866==    by 0x51B63B8: _IO_file_xsgetn (fileops.c:1438)
==1866==    by 0x51AB93E: fread (iofread.c:42)
==1866==    by 0x4007C3: main (recover2.c:31)
==1866==  Address 0xfff001000 is not stack'd, malloc'd or (recently) free'd
==1866== 
==1866== Jump to the invalid address stated on the next line
==1866==    at 0x0: ???
==1866==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==1866== 
==1866== 
==1866== Process terminating with default action of signal 11 (SIGSEGV)
==1866==  Bad permissions for mapped region at address 0x0
==1866==    at 0x0: ???

我是新手,仍在学习有关内存等方面的知识,因此希望能得到帮助,了解为什么它出现了问题。


请注意,在获得答案后不要大幅修改您的问题。无论如何,您正在将8个字符打印到“title”中,而它只能容纳7个字符。 - Jongware
2个回答

7
通过这样做
fread(buffer, sizeof(buffer), 512, file)

你正在要求fread读取512块,每块大小为 sizeof(buffer) 字节。也就是说,你试图将512 * 512 = 262144字节读入一个声明为 uint8_t buffer [512] 的数组中,那肯定是不行的。
如果你只想将数据读入buffer数组中,可以选择以下方式之一:
fread(buffer, sizeof buffer, 1, file)

或者
fread(buffer, 1, sizeof buffer, file)

或者,如果你更喜欢的话,

fread(buffer, sizeof *buffer, sizeof buffer / sizeof *buffer, file)

取决于您在读取操作中认为的“原子”数据块。


此外,

sprintf(title, "%03d.jpg", currentImage);

将生成至少7个字符长的字符串(例如001.jpg),这意味着title必须至少为8个字符以容纳零终止符。然而,您的title被声明为

char title[7];

这个太小了。


嗨AnT,感谢您指出这个错误。我已经将它更改为fread(buffer,sizeof buffer,1,file),但不幸的是仍然出现分段错误。 - edd91
嗨AnT,我已经在我的原始帖子中包含了新的valgrind错误。谢谢。 - edd91
@edd91:你的代码不完整,这使得无法完全分析它。然而,正如我上面所添加的,你的“title”需要更大。 - AnT stands with Russia
嗨@ant,谢谢你的帮助!现在已经修好了,标题的问题解决后就可以解决第一个问题。为什么我需要在标题中留出\0的空间而缓冲区不需要?即为什么缓冲区不应该是513? - edd91
@edd91:函数sprintf在目标缓冲区中构建一个字符串。这意味着它将字符写入缓冲区,然后用终止符\0字符进行封顶。这就是在C语言中构建字符串的方式。一个长度为7个字符的字符串需要8个字符来存储。与此同时,fread函数不适用于字符串,并且对字符串一无所知。它只是从文件中读取字节,读取您要求它读取的那么多字节。它不会尝试使用任何额外的字符来封顶这些字节。您要求它读取512字节-它确切地读取了512字节。它不会做其他任何事情。 - AnT stands with Russia
谢谢 @ant,非常感谢。 - edd91

1
根据 man pagefread() 的描述,函数签名为:

size_t fread(void *ptr, size_t size, size_t nmemb, FILE * stream );

其中函数描述如下:

fread() 函数从指向 stream 的流中读取 nmemb 个数据元素,每个元素大小为 size 字节,并将它们存储在由 ptr 指向的位置。

因此,你的代码应该是:
 while (fread(buffer, sizeof(buffer[0]), 512, file) == 1)

否则,你最终会请求读取并存储512个大小为512字节块,这是错误的,并且会导致缓冲区溢出,正如valgrind所报告的那样。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接