的声明fread
如下:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
问题是:两次调用fread
:
char a[1000];
fread(a, 1, 1000, stdin);
fread(a, 1000, 1, stdin);
会一次读取一次1000
字节吗?
Answers:
性能可能有差异,也可能没有差异。语义上有所不同。
fread(a, 1, 1000, stdin);
尝试读取1000个数据元素,每个元素长1个字节。
fread(a, 1000, 1, stdin);
尝试读取1个1000字节长的数据元素。
它们不同,因为fread()
返回的是它能够读取的数据元素的数量,而不是字节的数量。如果在读取完整的1000个字节之前到达文件末尾(或错误状态),则第一个版本必须确切说明读取的字节数。第二个只是失败并返回0。
实际上,可能只是调用一个较低级别的函数,该函数尝试读取1000个字节并指示实际读取了多少个字节。对于较大的读取,它可能会发出多个较低级别的调用。要返回的值的计算fread()
方法不同,但是计算费用微不足道。
如果实现可以在尝试读取数据之前告诉您没有足够的数据可读取,则可能会有所不同。例如,如果您正在读取900字节的文件,则第一个版本将读取所有900字节并返回900,而第二个版本可能不会费心地读取任何内容。在这两种情况下,文件位置指示器都以成功读取的字符数(即900 )前移。
但是通常,您可能应该根据需要的信息来选择如何调用它。如果部分读取并不比完全不读取任何数据好,则读取单个数据元素。如果部分读取很有用,则以较小的块读取。
ftell
请先致电fread
,然后致电fseek
。
根据规范,两者的实现方式可能会有所不同。
如果文件少于1000个字节,fread(a, 1, 1000, stdin)
(读取1000个元素,每个元素1个字节)仍将复制所有字节,直到EOF。另一方面,未指定fread(a, 1000, 1, stdin)
存储(读取1个1000字节元素)的结果a
,因为没有足够的数据来完成对“第一个”(也是唯一的)1000字节元素的读取。
当然,某些实现可能仍会根据需要将“ partial”元素复制到任意多个字节中。
那将是实现细节。glibc中,两者在相同的性能,因为它基本上实现为(参考http://sourceware.org/git/?p=glibc.git;a=blob;f=libio/iofread.c):
size_t fread (void* buf, size_t size, size_t count, FILE* f)
{
size_t bytes_requested = size * count;
size_t bytes_read = read(f->fd, buf, bytes_requested);
return bytes_read / size;
}
注意C 和POSIX标准不保证size
每次都需要阅读完整的尺寸对象。如果无法读取完整的对象(例如,stdin
只有999个字节,但您已请求size == 1000
),则文件将处于不确定状态(C99§7.19.8.1/ 2)。
编辑:请参阅有关POSIX的其他答案。
fread
getc
内部通话。在Minix
次数getc
被称为仅仅是size*nmemb
那么多少次getc
将被称为依赖于产品的这两个。所以无论fread(a, 1, 1000, stdin)
和fread(a, 1000, 1, stdin)
运行getc
1000=(1000*1)
时间。这是fread
Minix的简单实现
size_t fread(void *ptr, size_t size, size_t nmemb, register FILE *stream){
register char *cp = ptr;
register int c;
size_t ndone = 0;
register size_t s;
if (size)
while ( ndone < nmemb ) {
s = size;
do {
if ((c = getc(stream)) != EOF)
*cp++ = c;
else
return ndone;
} while (--s);
ndone++;
}
return ndone;
}
还有一个句子形式http://pubs.opengroup.org/onlinepubs/000095399/functions/fread.html值得注意
fread()函数应从stream指向的流中读取ptr指向的数组,直到nitems个元素,其大小由以字节为单位的大小指定。对于每个对象,应调用fgetc()函数进行大小调用,并将结果按读取顺序存储在与对象完全重叠的无符号字符数组中。
简而言之,两种情况下数据都可以通过fgetc()进行访问!
fgetc
,因此Posix确实与C99保持一致。但是该标准并没有为符合标准的程序提供任何方法来确定是否fgetc
“真正”被调用,或者是否fread
具有其他等同功能。5.1.2.3解释说,该标准仅描述了“抽象机器”的行为,并列出了实际程序必须以何种方式匹配该行为。这在C ++中被称为“假设”规则,但在C中则没有(我之前的错误)。不可观察的行为不必相同。
fgetc
调用次数(也许通过使您的程序与该函数自己的版本链接,例如通过修改和重新编译libc),也可以通过警告来做到这一点。并非总是调用您要替换的函数,只有当标准将抽象机描述为调用它时,才调用该函数。
fread
(或C代码的其他任何部分)效果的便捷方法。在Posix中以这种方式进行记录只是因为在标准中以这种方式进行了记录。
我想在这里澄清答案。fread执行缓冲的IO。实际使用的读取块大小由使用的C实现确定。
通过以下两个调用,所有现代C库都将具有相同的性能:
fread(a, 1, 1000, file);
fread(a, 1000, 1, file);
甚至像:
for (int i=0; i<1000; i++)
a[i] = fgetc(file)
应该会得到相同的磁盘访问模式,尽管由于更多的对标准c库的调用而使fgetc变慢,并且在某些情况下需要磁盘来执行其他搜索,而磁盘会另外被优化。
回到两种形式的区别。前者返回读取的实际字节数。如果文件大小小于1000,则后者返回0,否则返回1。在两种情况下,缓冲区都将填充相同的数据,即文件内容最多1000个字节。
通常,您可能希望将2nd参数(大小)设置为1,以便获得读取的字节数。
fread(a, 1000, N, stdin);
总是将fp指标提高1000
?的倍数吗?