⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chapter8.html

📁 c语言编程-c语言作者的书。英文原版。非常好。
💻 HTML
📖 第 1 页 / 共 4 页
字号:
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 &lt;stdio.h&gt;   #include &lt;stdarg.h&gt;   /* 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 &gt;&gt; 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) &gt;= 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>&lt;stdio.h&gt;</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>&lt;stdio.h&gt;</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)-&gt;flag & _EOF) != 0)   #define ferror(p)   ((p)-&gt;flag & _ERR) != 0)   #define fileno(p)   ((p)-&gt;fd)   #define getc(p)   (--(p)-&gt;cnt &gt;= 0 \                  ? (unsigned char) *(p)-&gt;ptr++ : _fillbuf(p))   #define putc(x,p) (--(p)-&gt;cnt &gt;= 0 \                  ? *(p)-&gt;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 &lt;fcntl.h&gt;   #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 &lt; _iob + OPEN_MAX; fp++)           if ((fp-&gt;flag & (_READ | _WRITE)) == 0)               break;        /* found free slot */       if (fp &gt;= _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-&gt;fd = fd;       fp-&gt;cnt = 0;       fp-&gt;base = NULL;       fp-&gt;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-&gt;flag&(_READ|_EOF_ERR)) != _READ)           return EOF;       bufsize = (fp-&gt;flag & _UNBUF) ? 1 : BUFSIZ;       if (fp-&gt;base == NULL)     /* no buffer yet */           if ((fp-&gt;base = (char *) malloc(bufsize)) == NULL)               return EOF;       /* can't get buffer */       fp-&gt;ptr = fp-&gt;base;       fp-&gt;cnt = read(fp-&gt;fd, fp-&gt;ptr, bufsize);       if (--fp-&gt;cnt &lt; 0) {           if (fp-&gt;cnt == -1)               fp-&gt;flag |= _EOF;           else               fp-&gt;flag |= _ERR;           fp-&gt;cnt = 0;           return EOF;       }       return (unsigned char) *fp-&gt;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 + -