📄 chapter8.html
字号:
to use another member of the <tt>printf</tt> family. The standard libraryfunction <tt>vprintf</tt> is like <tt>printf</tt> except that the variableargument list is replaced by a single argument that has been initialized bycalling the <tt>va_start</tt> macro. Similarly, <tt>vfprintf</tt> and<tt>vsprintf</tt> match <tt>fprintf</tt> and <tt>sprintf</tt>.<pre> #include <stdio.h> #include <stdarg.h> /* error: print an error message and die */ void error(char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "error: "); vprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); exit(1); }</pre>There is a limit (often about 20) on the number of files that a program mayopen simultaneously. Accordingly, any program that intends to process manyfiles must be prepared to re-use file descriptors. The function<tt>close(int fd)</tt> breaks the connection between a file descriptor and anopen file, and frees the file descriptor for use with some other file; itcorresponds to <tt>fclose</tt> in the standard library except that there is nobuffer to flush. Termination of a program via <tt>exit</tt> or return from themain program closes all open files.<p>The function <tt>unlink(char *name)</tt> removes the file <tt>name</tt> from thefile system. It corresponds to the standard library function <tt>remove</tt>.<p><strong>Exercise 8-1.</strong> Rewrite the program <tt>cat</tt> from<a href="chapter7.html">Chapter 7</a> using <tt>read</tt>, <tt>write</tt>,<tt>open</tt>, and <tt>close</tt> instead of their standard libraryequivalents. Perform experiments to determine the relative speeds of the twoversions.<h2><a name="s8.4">8.4 Random Access - Lseek</a></h2>Input and output are normally sequential: each <tt>read</tt> or <tt>write</tt>takes place at a position in the file right after the previous one. Whennecessary, however, a file can be read or written in any arbitrary order. Thesystem call <tt>lseek</tt> provides a way to move around in a file withoutreading or writing any data:<pre> long lseek(int fd, long offset, int origin);</pre>sets the current position in the file whose descriptor is <tt>fd</tt> to<tt>offset</tt>, which is taken relative to the location specified by <tt>origin</tt>.Subsequent reading or writing will begin at that position. <tt>origin</tt> canbe 0, 1, or 2 to specify that <tt>offset</tt> is to be measured from thebeginning, from the current position, or from the end of the filerespectively. For example, to append to a file (the redirection >> in theUNIX shell, or <tt>"a"</tt> for <tt>fopen</tt>), seek to the end before writing:<pre> lseek(fd, 0L, 2);</pre>To get back to the beginning (``rewind''),<pre> lseek(fd, 0L, 0);</pre>Notice the <tt>0L</tt> argument; it could also be written as <tt>(long) 0</tt>or just as <tt>0</tt> if <tt>lseek</tt> is properly declared.<p>With <tt>lseek</tt>, it is possible to treat files more or less like arrays, atthe price of slower access. For example, the following function reads anynumber of bytes from any arbitrary place in a file. It returns the numberread, or <tt>-1</tt> on error.<pre> #include "syscalls.h" /*get: read n bytes from position pos */ int get(int fd, long pos, char *buf, int n) { if (lseek(fd, pos, 0) >= 0) /* get to pos */ return read(fd, buf, n); else return -1; }</pre>The return value from <tt>lseek</tt> is a long that gives the new position inthe file, or <tt>-1</tt> if an error occurs. The standard library function<tt>fseek</tt> is similar to <tt>lseek</tt> except that the first argument isa <tt>FILE *</tt> and the return is non-zero if an error occurred.<h2><a name="s8.5">8.5 Example - An implementation of Fopen and Getc</a></h2>Let us illustrate how some of these pieces fit together by showing animplementation of the standard library routines <tt>fopen</tt> and <tt>getc</tt>.<p>Recall that files in the standard library are described by file pointersrather than file descriptors. A file pointer is a pointer to a structure thatcontains several pieces of information about the file: a pointer to a buffer,so the file can be read in large chunks; a count of the number of charactersleft in the buffer; a pointer to the next character position in the buffer;the file descriptor; and flags describing read/write mode, error status, etc.<p>The data structure that describes a file is contained in <tt><stdio.h></tt>,which must be included (by <tt>#include</tt>) in any source file that usesroutines from the standard input/output library. It is also included byfunctions in that library. In the following excerpt from a typical<tt><stdio.h></tt>, names that are intended for use only by functions ofthe library begin with an underscore so they are less likely to collide withnames in a user's program. This convention is used by all standard libraryroutines.<pre> #define NULL 0 #define EOF (-1) #define BUFSIZ 1024 #define OPEN_MAX 20 /* max #files open at once */ typedef struct _iobuf { int cnt; /* characters left */ char *ptr; /* next character position */ char *base; /* location of buffer */ int flag; /* mode of file access */ int fd; /* file descriptor */ } FILE; extern FILE _iob[OPEN_MAX]; #define stdin (&_iob[0]) #define stdout (&_iob[1]) #define stderr (&_iob[2]) enum _flags { _READ = 01, /* file open for reading */ _WRITE = 02, /* file open for writing */ _UNBUF = 04, /* file is unbuffered */ _EOF = 010, /* EOF has occurred on this file */ _ERR = 020 /* error occurred on this file */ }; int _fillbuf(FILE *); int _flushbuf(int, FILE *); #define feof(p) ((p)->flag & _EOF) != 0) #define ferror(p) ((p)->flag & _ERR) != 0) #define fileno(p) ((p)->fd) #define getc(p) (--(p)->cnt >= 0 \ ? (unsigned char) *(p)->ptr++ : _fillbuf(p)) #define putc(x,p) (--(p)->cnt >= 0 \ ? *(p)->ptr++ = (x) : _flushbuf((x),p)) #define getchar() getc(stdin) #define putcher(x) putc((x), stdout)</pre>The <tt>getc</tt> macro normally decrements the count, advances the pointer, andreturns the character. (Recall that a long <tt>#define</tt> is continued with abackslash.) If the count goes negative, however, <tt>getc</tt> calls the function<tt>_fillbuf</tt> to replenish the buffer, re-initialize the structure contents,and return a character. The characters are returned <tt>unsigned</tt>, whichensures that all characters will be positive.<p>Although we will not discuss any details, we have included the definition of<tt>putc</tt> to show that it operates in much the same way as <tt>getc</tt>, callinga function <tt>_flushbuf</tt> when its buffer is full. We have also includedmacros for accessing the error and end-of-file status and the file descriptor.<p>The function <tt>fopen</tt> can now be written. Most of <tt>fopen</tt> isconcerned with getting the file opened and positioned at the right place, andsetting the flag bits to indicate the proper state. <tt>fopen</tt> does notallocate any buffer space; this is done by <tt>_fillbuf</tt> when the file isfirst read.<pre> #include <fcntl.h> #include "syscalls.h" #define PERMS 0666 /* RW for owner, group, others */ FILE *fopen(char *name, char *mode) { int fd; FILE *fp; if (*mode != 'r' && *mode != 'w' && *mode != 'a') return NULL; for (fp = _iob; fp < _iob + OPEN_MAX; fp++) if ((fp->flag & (_READ | _WRITE)) == 0) break; /* found free slot */ if (fp >= _iob + OPEN_MAX) /* no free slots */ return NULL; if (*mode == 'w') fd = creat(name, PERMS); else if (*mode == 'a') { if ((fd = open(name, O_WRONLY, 0)) == -1) fd = creat(name, PERMS); lseek(fd, 0L, 2); } else fd = open(name, O_RDONLY, 0); if (fd == -1) /* couldn't access name */ return NULL; fp->fd = fd; fp->cnt = 0; fp->base = NULL; fp->flag = (*mode == 'r') ? _READ : _WRITE; return fp; }</pre>This version of <tt>fopen</tt> does not handle all of the access modepossibilities of the standard, though adding them would not take much code.In particular, our <tt>fopen</tt> does not recognize the ``<tt>b</tt>'' thatsignals binary access, since that is meaningless on UNIX systems, nor the``<tt>+</tt>'' that permits both reading and writing.<p>The first call to <tt>getc</tt> for a particular file finds a count of zero,which forces a call of <tt>_fillbuf</tt>. If <tt>_fillbuf</tt> finds that the fileis not open for reading, it returns <tt>EOF</tt> immediately. Otherwise, it triesto allocate a buffer (if reading is to be buffered).<p>Once the buffer is established, <tt>_fillbuf</tt> calls <tt>read</tt> to fill it,sets the count and pointers, and returns the character at the beginning ofthe buffer. Subsequent calls to <tt>_fillbuf</tt> will find a buffer allocated.<pre> #include "syscalls.h" /* _fillbuf: allocate and fill input buffer */ int _fillbuf(FILE *fp) { int bufsize; if ((fp->flag&(_READ|_EOF_ERR)) != _READ) return EOF; bufsize = (fp->flag & _UNBUF) ? 1 : BUFSIZ; if (fp->base == NULL) /* no buffer yet */ if ((fp->base = (char *) malloc(bufsize)) == NULL) return EOF; /* can't get buffer */ fp->ptr = fp->base; fp->cnt = read(fp->fd, fp->ptr, bufsize); if (--fp->cnt < 0) { if (fp->cnt == -1) fp->flag |= _EOF; else fp->flag |= _ERR; fp->cnt = 0; return EOF; } return (unsigned char) *fp->ptr++; }</pre>The only remaining loose end is how everything gets started. The array<tt>_iob</tt> must be defined and initialized for <tt>stdin</tt>,<tt>stdout</tt> and <tt>stderr</tt>:<pre> FILE _iob[OPEN_MAX] = { /* stdin, stdout, stderr */ { 0, (char *) 0, (char *) 0, _READ, 0 }, { 0, (char *) 0, (char *) 0, _WRITE, 1 }, { 0, (char *) 0, (char *) 0, _WRITE, | _UNBUF, 2 } };</pre>The initialization of the <tt>flag</tt> part of the structure shows that <tt>stdin</tt> is to be read, <tt>stdout</tt> is to be written, and<tt>stderr</tt> is to be written unbuffered.<p><strong>Exercise 8-2.</strong> Rewrite <tt>fopen</tt> and <tt>_fillbuf</tt>with fields instead of explicit bit operations. Compare code size andexecution speed.<p><strong>Exercise 8-3.</strong> Design and write <tt>_flushbuf</tt>,<tt>fflush</tt>, and <tt>fclose</tt>.<p><strong>Exercise 8-4.</strong> The standard library function<pre>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -