C fread/fwrite将大小和计数作为参数的理由是什么?

C fread/fwrite将大小和计数作为参数的理由是什么?,c,file-io,libc,C,File Io,Libc,我们在这里讨论了为什么fread()和fwrite()采用每个成员的大小,计算并返回读/写的成员数,而不仅仅是采用缓冲区和大小。我们能想到的唯一用途是,如果您想读/写一组结构,这些结构不能被平台对齐均匀分割,因此已填充,但不能如此常见,从而保证设计中的这种选择 发件人: 函数fread()读取数据的nmemb元素,每个元素的大小为字节长, 从流指向的流,将它们存储在给定的位置 通过ptr 函数fwrite()写入数据的nmemb元素,每个元素大小为个字节 long,指向流指向的流,从位置获取它们

我们在这里讨论了为什么fread()和fwrite()采用每个成员的大小,计算并返回读/写的成员数,而不仅仅是采用缓冲区和大小。我们能想到的唯一用途是,如果您想读/写一组结构,这些结构不能被平台对齐均匀分割,因此已填充,但不能如此常见,从而保证设计中的这种选择

发件人:

函数fread()读取数据的nmemb元素,每个元素的大小为字节长, 从流指向的流,将它们存储在给定的位置 通过ptr

函数fwrite()写入数据的nmemb元素,每个元素大小为个字节 long,指向流指向的流,从位置获取它们 由ptr提供

fread()和fwrite()返回成功读取或写入的项目数 (即,不是字符数)。如果发生错误,或 到达文件末尾时,返回值为短项计数(或零)


fread(buf,1000,1,stream)
fread(buf,1,1000,stream)
的区别在于,在第一种情况下,如果文件较小,则只能得到一块1000字节的数据,或者什么都没有,而在第二种情况下,文件中的所有数据都小于或等于1000字节。

这取决于实现方式

单一UNIX规范说

对于每个物体,尺寸要求应为: 对fgetc()函数和 结果以读取顺序存储在 无符号字符的数组 覆盖对象

也有以下说明:

由于fgetc()对字节进行操作, 读一个由 多字节(或“多字节 字符“)可能需要多次调用 到fgetc()

当然,这早于像UTF-8这样的奇特的可变字节字符编码


SUS指出,这实际上是从ISO C文档中提取的。

很可能要追溯到文件I/O的实现方式。(回到过去)在块中写入/读取文件可能比一次写入所有文件更快。

这里,让我修复这些函数:

size_t fread_buf( void* ptr, size_t size, FILE* stream)
{
    return fread( ptr, 1, size, stream);
}


size_t fwrite_buf( void const* ptr, size_t size, FILE* stream)
{
    return fwrite( ptr, 1, size, stream);
}
至于
fread()
/
fwrite()
参数的基本原理,我很久以前就丢失了我的K&R副本,所以我只能猜测。我认为一个可能的答案是Kernighan和Ritchie可能只是认为在对象数组上执行二进制I/O是最自然的。此外,他们可能认为在某些体系结构上,块I/O实现起来更快/更容易,或者更容易

尽管C标准规定,
fread()
fwrite()
可以根据
fgetc()
fputc()
来实现,但请记住,该标准是在K&R定义C之后很久才出现的,并且标准中规定的内容可能不在最初设计者的想法中。甚至有可能K&R的“C编程语言”中所说的话可能与该语言最初设计时不一样

最后,以下是p.J.Plauger在“标准C库”中对
fread()
的看法:

如果
size
(秒)参数大于1,则无法确定 该函数是否还可以读取超出其报告范围的最多为
size-1
的其他字符。 通常,最好将函数调用为
fread(buf,1,size*n,stream)而不是
fread(buf,大小,n,流)


基本上,他是说
fread()
的接口坏了。对于
fwrite()
,他指出,“写错误通常很少见,所以这不是一个主要缺点”——我不同意这一说法。

这纯粹是猜测,但在过去(有些仍然存在)许多文件系统不是硬盘上的简单字节流


许多文件系统都是基于记录的,因此为了有效地满足这些文件系统的要求,您必须指定项目(“记录”)的数量,从而允许fwrite/fread在存储上作为记录而不仅仅是字节流进行操作。

我想这是因为C缺少函数重载。如果有的话,那么尺寸将是多余的。但在C语言中,不能确定数组元素的大小,必须指定一个

考虑这一点:

int intArray[10];
fwrite(intArray, sizeof(int), 10, fd);
如果fwrite接受字节数,则可以写入以下内容:

int intArray[10];
fwrite(intArray, sizeof(int)*10, fd);
但它只是效率低下。您将有sizeof(int)乘以更多的系统调用

应该考虑的另一点是,您通常不希望将数组元素的一部分写入文件。你需要整数,否则什么都不要。fwrite返回成功写入的许多元素。所以,如果您发现一个元素只写入了2个低字节,您会怎么做


在某些系统上(由于对齐),如果不创建副本并进行移位,就无法访问整数的一个字节。

对于可以避免读取任何部分记录的实现来说,大小和计数有单独的参数可能是有利的。如果要从管道之类的对象中使用单字节读取,即使使用的是固定格式的数据,也必须考虑记录在两次读取中被分割的可能性。如果可以改为请求(例如,当有293个字节可用时,非阻塞读取最多40个记录,每个记录10个字节),并让系统返回290个字节(29个完整记录),同时保留3个字节以备下次读取,则会方便得多


我不知道fread的实现在多大程度上可以处理这样的语义,但它们在承诺支持它们的实现上肯定很方便。

嘿,这是个好问题。我一直在想这件事,请看看这个帖子:不是真的。fwrite的C规范指出,它重复调用fputc:虽然是真的,但只有te