我喜欢使用C语言中读写文件的两个函数fread和fwrite,而不喜欢使用C++中的ifstream和ofstream类,一来用起来感觉麻烦,二来运行效率还低。
一直对fread和fwrite的参数列表中的size和nmemb很迷惑,你看系统API函数write的定义:
ssize_t write(int fd, const void *buf, size_t count);
一看就能明白,向文件中写数据,文件是fd,数据地址为buf,长度是count个字节。而fwrite的就不一样,如下:
size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream);
文件是stream,数据地址为ptr,长度却是nmemb个size大小,为嘛不弄成size * nmemb
成一个参数,而要弄成两个。我开始以为是跟位置对齐有关,而且认为
fwrite(ptr, 1, 4096, fp);
会比
fwrite(ptr, 4096, 1, fp);
效率高。今天查了一下glibc源码,才知道,这种理解是错误的,这么搞两个参数跟效率无关,而跟返回结果有关。先直接上源码,注意fwrite的具体实现是这个叫_IO_fwrite的函数:
_IO_size_t
_IO_fwrite (buf, size, count, fp)
const void *buf;
_IO_size_t size;
_IO_size_t count;
_IO_FILE *fp;
{
_IO_size_t request = size * count;
_IO_size_t written = 0;
CHECK_FILE (fp, 0);
if (request == 0)
return 0;
_IO_acquire_lock (fp);
if (_IO_vtable_offset (fp) != 0 || _IO_fwide (fp, -1) == -1)
written = _IO_sputn (fp, (const char *) buf, request);
_IO_release_lock (fp);
/* We have written all of the input in case the return value indicates
this or EOF is returned. The latter is a special case where we
simply did not manage to flush the buffer. But the data is in the
buffer and therefore written as far as fwrite is concerned. */
if (written == request || written == EOF)
return count;
else
return written / size;
}
从源码可以看到,两个参数传进去之后,还是算了size * count
,所以与效率无关。而在返回时,如果整个都写成功,那就返回count,否则返回written / size
,总之返回的是成功写入的块数,而不是字节数(除非size为1)。好处是写入多个结构体时,返回值能告诉你成功写入的结构体的个数,这只是让调用者方便了些许而已,如果调用者关心的是全部是否写成功,那么完全没必要纠结于是1, 4096还是4096, 1。fread参数列表一样,道理一样。
通过源码还可以注意到,写文件是有加锁的。而且写是不会因为遇到EOF而失败的,遇到EOF是因为没有清buffer,但数据肯定是到buffer了。fputs可以算是一个参数简化版的fwrite,看看实现代码就能明白了:
int
_IO_fputs (str, fp)
const char *str;
_IO_FILE *fp;
{
_IO_size_t len = strlen (str);
int result = EOF;
CHECK_FILE (fp, EOF);
_IO_acquire_lock (fp);
if ((_IO_vtable_offset (fp) != 0 || _IO_fwide (fp, -1) == -1)
&& _IO_sputn (fp, str, len) == len)
result = 1;
_IO_release_lock (fp);
return result;
}