Cha*_*ase 6 c binary file cs50
我有一个相当奇怪的问题,实际上根本不是很实用。错误(以r模式读取二进制文件)显而易见,但我对其他事情感到困惑。
这是代码-
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdint.h>
#define BUFFER_LEN 512
typedef uint8_t BYTE;
int main()
{
FILE* memcard = fopen("card.raw", "r");
BYTE buffer[BUFFER_LEN];
int count = 0;
while (fread(buffer, sizeof(*buffer), BUFFER_LEN, memcard) != 0)
{
printf("count: %d\n", count++);
}
fclose(memcard);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
现在,card.raw是一个二进制文件,所以这个读取会出错,因为是以rmode 而不是rb. 但我很好奇的是,该循环正好执行了 3 次,在最终执行中,它甚至没有读取 512 个字节。
现在,如果我将该循环更改为
while (fread(buffer, sizeof(*buffer), BUFFER_LEN, memcard) != 0)
{
printf("ftell: %ld\n", ftell(memcard));
}
Run Code Online (Sandbox Code Playgroud)
它不再在执行 3 次时停止。事实上,它一直持续到(大概)文件结束。该fread计数仍搞砸。许多读取不会在读取元素时返回 512。但这很可能是由于文件以r模式打开以及它伴随的所有编码错误。
ftell不应该影响文件本身,那么为什么包含ftell在循环中使它执行更多次?
我决定稍微改变循环以提取更多信息-
while (fread(buffer, sizeof(*buffer), BUFFER_LEN, memcard) != 0)
{
printf("ftell: %ld\n", ftell(memcard));
}
Run Code Online (Sandbox Code Playgroud)
如果ftell包含在循环中并且前几个结果看起来像 -
现在如果我完全删除那条ftell线,它会给我 -
只有 3 次处决,但没有任何改变。
这种行为背后的解释是什么?
注意:我知道两者都返回的计数fread并且ftell由于读取模式可能是错误的,但这不是我关心的问题。我只是好奇 - 为什么包括ftell和不包括它之间的区别。
此外,如果有帮助,该card.raw文件实际上只是 cs50 pset4“存储卡”。您可以通过wget https://cdn.cs50.net/2019/fall/psets/4/recover/recover.zip并将输出文件存储在.zip
编辑:我应该提到这是在 Windows 上并使用 VS2019 的 clang 工具。命令行选项(从 VS2019 项目属性检查)看起来像 -
/permissive- /GS /W3 "Debug\" "Debug\" /Zi /Od "Debug\vc142.pdb" /fp:precise /D "_CRT_SECURE_NO_WARNINGS" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /WX- /Gd /MDd /Fa"Debug\" /EHsc /nologo /Fo"Debug\" /Fp"Debug\Test.pch" /diagnostics:column
Run Code Online (Sandbox Code Playgroud)
编辑:另外,我确实检查ferror了循环内部,有和没有ftell,根本没有错误。事实上,feof在这两种情况下,循环后都返回 1。
编辑:我也尝试memcard == NULL在fopen相同的行为之后添加一个检查。
编辑:通过@orlp 解决答案。事实上,我确实检查了错误。不过,我绝对应该发布它。
while ((count = fread(buffer, sizeof(*buffer), BUFFER_LEN, memcard)) != 0)
{
printf("fread bytes read: %d\n", count);
printf("ftell: %ld\n", ftell(memcard));
}
Run Code Online (Sandbox Code Playgroud)
这两个if语句都没有被触发。
编辑:我以为我们已经得到了答案,它正在ftell重置 EOF。但我改变了循环——
/permissive- /GS /W3 "Debug\" "Debug\" /Zi /Od "Debug\vc142.pdb" /fp:precise /D "_CRT_SECURE_NO_WARNINGS" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /WX- /Gd /MDd /Fa"Debug\" /EHsc /nologo /Fo"Debug\" /Fp"Debug\Test.pch" /diagnostics:column
Run Code Online (Sandbox Code Playgroud)
这会触发第一个if(feof)和第二个if(feof)
正如预期的那样,如果我将 更改ftell为fseek(memcard, 0, SEEK_CUR),EOF 则重置并且reached after永远不会打印 。
正如一些评论者指出的那样,它遇到了EOF, 并且ftell实际上摆脱了 EOF。为什么?为了找到答案,我们必须查看 glibc 的源代码。我们可以找到以下来源ftell: :
long int
_IO_ftell (FILE *fp)
{
off64_t pos;
CHECK_FILE (fp, -1L);
_IO_acquire_lock (fp);
pos = _IO_seekoff_unlocked (fp, 0, _IO_seek_cur, 0);
if (_IO_in_backup (fp) && pos != _IO_pos_BAD)
{
if (_IO_vtable_offset (fp) != 0 || fp->_mode <= 0)
pos -= fp->_IO_save_end - fp->_IO_save_base;
}
_IO_release_lock (fp);
if (pos == _IO_pos_BAD)
{
if (errno == 0)
__set_errno (EIO);
return -1L;
}
if ((off64_t) (long int) pos != pos)
{
__set_errno (EOVERFLOW);
return -1L;
}
return pos;
}
libc_hidden_def (_IO_ftell)
weak_alias (_IO_ftell, ftell)
Run Code Online (Sandbox Code Playgroud)
这是重要的一行:
pos = _IO_seekoff_unlocked (fp, 0, _IO_seek_cur, 0);
Run Code Online (Sandbox Code Playgroud)
让我们找到源_IO_seekoff_unlocked:
off64_t
_IO_seekoff_unlocked (FILE *fp, off64_t offset, int dir, int mode)
{
if (dir != _IO_seek_cur && dir != _IO_seek_set && dir != _IO_seek_end)
{
__set_errno (EINVAL);
return EOF;
}
/* If we have a backup buffer, get rid of it, since the __seekoff
callback may not know to do the right thing about it.
This may be over-kill, but it'll do for now. TODO */
if (mode != 0 && ((_IO_fwide (fp, 0) < 0 && _IO_have_backup (fp))
|| (_IO_fwide (fp, 0) > 0 && _IO_have_wbackup (fp))))
{
if (dir == _IO_seek_cur && _IO_in_backup (fp))
{
if (_IO_vtable_offset (fp) != 0 || fp->_mode <= 0)
offset -= fp->_IO_read_end - fp->_IO_read_ptr;
else
abort ();
}
if (_IO_fwide (fp, 0) < 0)
_IO_free_backup_area (fp);
else
_IO_free_wbackup_area (fp);
}
return _IO_SEEKOFF (fp, offset, dir, mode);
}
Run Code Online (Sandbox Code Playgroud)
基本上,它只是做一些检查然后调用_IO_SEEKOFF,所以让我们找到它的来源:
/* The 'seekoff' hook moves the stream position to a new position
relative to the start of the file (if DIR==0), the current position
(MODE==1), or the end of the file (MODE==2).
It matches the streambuf::seekoff virtual function.
It is also used for the ANSI fseek function. */
typedef off64_t (*_IO_seekoff_t) (FILE *FP, off64_t OFF, int DIR,
int MODE);
#define _IO_SEEKOFF(FP, OFF, DIR, MODE) JUMP3 (__seekoff, FP, OFF, DIR, MODE)
Run Code Online (Sandbox Code Playgroud)
所以基本上,ftell最终调用了一个相当于fseek(fp, 0, SEEK_CUR). 在fseek标准中我们看到:“成功调用该fseek()函数会清除流的文件结束指示器。” 这就是ftell改变程序行为的原因。
| 归档时间: |
|
| 查看次数: |
208 次 |
| 最近记录: |