11.5.4 _read
_read的代码位于crt/src/read.c。在省略了一部分无关紧要的代码之后,其内容如下:
fread -> fread_s -> _fread_nolock_s -> _read: int __cdecl _read (int fh, void *buf, unsigned cnt) { int bytes_read; /* number of bytes read */ char *buffer; /* buffer to read to */ int os_read; /* bytes read on OS call */ char *p, *q; /* pointers into buffer */ char peekchr; /* peek-ahead character */ ULONG filepos; /* file position after seek */ ULONG dosretval; /* o.s. return value */ bytes_read = 0; /* nothing read yet */ buffer = buf; |
这部分是_read函数的参数、局部变量和初始化部分。下面的代码处理一个单字节缓冲:
if ((_osfile(fh) & (FPIPE|FDEV)) && _pipech(fh) != LF) { *buffer++ = _pipech(fh); ++bytes_read; --cnt; _pipech(fh) = LF; } |
if中的判断语句使得这段代码仅对设备和管道文件有效。对于设备和管道文件,ioinfo结构提供了一个单字节缓冲pipech字段用于处理一些特殊情况。宏_pipech返回这一字段:
#define _pipech(i) ( _pioinfo(i)->pipech ) |
pipech字段的值等于LF(即字符\n)的时候表明该缓冲无效,这样设计的原因是pipech的用途导致它永远不会被赋值为LF。我们将在稍后的部分里详细讨论这一话题。
_read函数在每次读取管道和设备数据的时候必须先检查pipech,以免漏掉一个字节。在处理完这个单字节缓冲之后,接下来的内容是实际的文件读取部分:
if ( !ReadFile( (HANDLE)_osfhnd(fh), buffer, cnt, (LPDWORD)&os_read, NULL ) ) { if ( (dosretval = GetLastError()) == ERROR_ACCESS_DENIED ) { errno = EBADF; _doserrno = dosretval; return -1; } else if ( dosretval == ERROR_BROKEN_PIPE ) { return 0; } else { _dosmaperr(dosretval); return -1; } } |
ReadFile是一个Windows API函数,由Windows系统提供,作用和_read类似,用于从文件里读取数据。在这里我们可以看到ReadFile接管了_read的第一个职责。在ReadFile返回之后,_read要检查其返回值。值得注意的是,Windows使用的函数返回值系统和crt使用的返回值系统是不同的,例如Windows使用ERROR_INVALID_PARAMETER(87)表示无效的参数,而CRT则用EBADF(9) 表示相同的信息。因此当ReadFile返回了错误信息之后,_read要把这个信息翻译为crt所使用的版本。_dosmaperr就是做这件工作的函数。在这里就不详细说明了。
【责任编辑:
云霞 TEL:(010)68476606】