fread是如何工作的?

fread的声明如下:

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

问题是:两个这样的fread调用的阅读性能有差异:

 char a[1000]; 
  1. fread(a, 1, 1000, stdin);
  2. fread(a, 1000, 1, stdin);

它会一次读取1000个字节吗?

performance可能有差异,也可能没有差别。 语义有所不同。

 fread(a, 1, 1000, stdin); 

试图读取1000个数据元素,每个数据元素是1个字节长。

 fread(a, 1000, 1, stdin); 

试图读取1个长度为1000字节的数据元素。

它们是不同的,因为fread()返回它能够读取的数据元素的数量,而不是字节数。 如果在读取完整的1000字节之前到达文件结尾(或错误条件),则第一个版本必须准确指示读取的字节数; 第二个失败并返回0。

在实践中,它可能只是调用一个较低级别的函数,试图读取1000个字节,并指出实际读取的字节数。 对于较大的读取,可能会进行多个较低级别的调用。 fread()返回的值的计算是不同的,但计算的代价是微不足道的。

如果实现可以在尝试读取数据之前知道没有足够的数据来读取,则可能有区别。 例如,如果您正在读取一个900字节的文件,则第一个版本将读取全部900个字节并返回900,而第二个版本可能不会读取任何内容。 在这两种情况下,文件位置指示符都被成功读取的字符数量提前,即900。

但总的来说,您应该根据您需要的信息来select如何调用它。 如果部分读取不比没有读取任何东西好,读取一个数据元素。 如果部分读取有用,请以较小的块读取。

根据规范 ,这两个可能会被不同的实施对待。

如果你的文件小于1000字节, fread(a, 1, 1000, stdin)fread(a, 1, 1000, stdin) (读取每个1000字节的1个字节)将仍然复制所有字节,直到EOF。 另一方面,存储在afread(a, 1000, 1, stdin) (读取1 1000个字节的元素) a是未指定的,因为没有足够的数据来完成读取'first'(且只有)1000字节元素。

当然,有些实现仍然可以根据需要将“部分”元素复制到多个字节中。

那将是实现细节。 在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; } 

请注意, 和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) Times。 这里是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; } 

可能没有性能差异,但这些调用是不一样的。

  • fread返回读取的元素的数量,所以这些调用将返回不同的值。
  • 如果一个元素不能完全读取,其值是不确定的:

如果发生错误,则stream的文件位置指示符的结果值是不确定的。 如果读取了部分元素,则其值是不确定的。 (ISO / IEC 9899:TC2 7.19.8.1)

glibc实现没有太大的区别,它只是将元素大小乘以元素的数量,以确定读取多less个字节,并最终分割由成员大小读取的数量。 但是指定元素大小为1的版本将始终告诉您读取的正确字节数。 但是,如果您只关心完全读取一定大小的元素,则使用另一种forms可以使您不必进行分割。

还有一个句子formshttp://pubs.opengroup.org/onlinepubs/000095399/functions/fread.html值得注意

fread()函数应该从stream所指向的stream中读入由ptr指向的数组,直到由size指定大小的nitems元素。 对于每个对象,应调用fgetc()函数进行大小调整 ,并按读取顺序将结果存储在一个无符号字符数组中,该数组正好覆盖该对象。

在这两种情况下,数据都将通过fgetc()来访问…!

我想在这里澄清答案。 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) 

应该导致相同的磁盘访问模式,尽pipe由于更多的调用到标准的c库中,fgetc会变得更慢,在某些情况下需要磁盘来执行本来可以优化的附加search。

回到两种forms的fread之间的区别。 前者返回读取的实际字节数。 如果文件大小小于1000,后者返回0,否则返回1.在这两种情况下,缓冲区将被填充相同的数据,即文件内容最多1000字节。

一般来说,您可能想要将第二个参数(大小)设置为1,以便获得读取的字节数。