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
字节 吗?
答案 0 :(得分:98)
表现可能有差异,也可能没有差异。语义有所不同。
fread(a, 1, 1000, stdin);
尝试读取1000个数据元素,每个数据元素长度为1个字节。
fread(a, 1000, 1, stdin);
尝试读取1个1000字节长的数据元素。
它们是不同的,因为fread()
返回它能够读取的数据元素的数量,而不是字节数。如果在读取完整的1000个字节之前它到达文件结尾(或错误条件),则第一个版本必须准确指出它读取的字节数;第二个失败并返回0。
实际上,它可能只是调用一个低级函数,它试图读取1000个字节并指示它实际读取的字节数。对于较大的读取,它可能会进行多个较低级别的调用。 fread()
返回的值的计算是不同的,但计算的费用是微不足道的。
如果实现可以在尝试读取数据之前判断没有足够的数据可供读取,则可能存在差异。例如,如果您正在读取900字节的文件,则第一个版本将读取所有900个字节并返回900,而第二个版本可能无法读取任何内容。在这两种情况下,文件位置指示符都按成功读取的字符的数量提前,即900。
但总的来说,你应该根据你需要的信息选择如何调用它。如果部分读取并不比不读取任何内容更好,则读取单个数据元素。如果部分读取有用,请读入较小的块。
答案 1 :(得分:16)
根据the specification,实施可以区别对待这两者。
如果您的文件小于1000字节,fread(a, 1, 1000, stdin)
(读取每个1字节的1000个元素)仍将复制所有字节,直到EOF。另一方面,fread(a, 1000, 1, stdin)
中存储的a
(读取1个1000字节元素)的结果未指定,因为没有足够的数据来完成读取“第一个”(并且只有)1000个字节元件。
当然,某些实现仍然可以将'partial'元素复制到所需的字节数。
答案 2 :(得分:13)
这将是实施细节。在glibc中,两者在性能上是相同的,因为它基本上实现为(Ref 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)。 / p>
编辑:查看有关POSIX的其他答案。
答案 3 :(得分:3)
可能没有性能差异,但这些调用并不相同。
fread
返回读取的元素数,因此这些调用将返回不同的值。如果发生错误,则流的文件位置指示符的结果值为 不定。如果读取了部分元素,则其值是不确定的。 (ISO / IEC 9899:TC2 7.19.8.1)
glibc implementation没有太大区别,它只是将元素大小乘以元素数量,以确定要读取的字节数,并将最终读取的成员数量除以。但是,指定元素大小为1的版本将始终告诉您读取的正确字节数。但是,如果您只关心完全读取特定大小的元素,则使用其他形式可以避免进行分割。
答案 4 :(得分:3)
fread
在内部调用getc
。在Minix
次调用getc
的次数仅为size*nmemb
,因此调用getc
的次数取决于这两者的产品。因此,fread(a, 1, 1000, stdin)
和fread(a, 1000, 1, stdin)
都会运行getc
1000=(1000*1)
次。
以下是Minix的fread
的简单实现
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;
}
答案 5 :(得分:1)
另一个句子http://pubs.opengroup.org/onlinepubs/000095399/functions/fread.html值得注意
fread()函数应读入ptr指向的数组,直到nitems元素,其大小由字节大小以字节为单位指定,来自stream指向的流。 对于每个对象,应按照读取顺序对fgetc()函数进行大小调用,结果存储,在无符号字符数组中完全覆盖对象。
两个案例数据中的inshort将由fgetc()...!
访问答案 6 :(得分:0)
我想澄清这里的答案。 fread执行缓冲IO。 fread使用的实际读取块大小由正在使用的C实现确定。
所有现代C库在两次调用时都具有相同的性能:
fread(a, 1, 1000, file);
fread(a, 1000, 1, file);
甚至像:
for (int i=0; i<1000; i++)
a[i] = fgetc(file)
应该导致相同的磁盘访问模式,尽管fgetc会因为对标准c库的更多调用而变慢,并且在某些情况下需要磁盘执行额外的搜索,否则这些搜索将被优化掉。
回到两种形式的恐惧之间的区别。前者返回读取的实际字节数。如果文件大小小于1000,后者返回0,否则返回1.在两种情况下,缓冲区将填充相同的数据,即文件的内容最多1000字节。
通常,您可能希望将第二个参数(大小)设置为1,以便获得读取的字节数。