欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C 標準I/O庫的粗略實現(xiàn)教程

 更新時間:2017年12月13日 08:47:48   作者:張雅宸  
下面小編就為大家分享一篇C 標準I/O庫的粗略實現(xiàn)教程,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧

寫一下fopen/getc/putc等C庫的粗略實現(xiàn),參考了K&R,但是有幾點根據(jù)自己理解的小改動,下面再具體說一下^_^

寫這篇文章主要是幫助自己理解下標準I/O庫大體是怎么工作的。

fopen與open之間的關系

操作系統(tǒng)提供的接口即為系統(tǒng)調(diào)用。而C語言為了讓用戶更加方便的編程,自己封裝了一些函數(shù),組成了C庫。而且不同的操作系統(tǒng)對同一個功能提供的系統(tǒng)調(diào)用可能不同,在不同的操作系統(tǒng)上C庫對用戶屏蔽了這些不同,所謂一次編譯處處運行。這里open為系統(tǒng)調(diào)用,fopen為C庫提供的調(diào)用。

image

C庫對的讀寫操作封裝了一個緩沖區(qū)。試想假如用戶頻繁的對文件讀寫少量字符,會頻繁的進行系統(tǒng)調(diào)用(read函數(shù)),而系統(tǒng)調(diào)用比較耗時。C庫自己封裝了一個緩沖區(qū),每次讀取特定數(shù)據(jù)量到緩沖區(qū),讀取時優(yōu)先從緩沖區(qū)讀取,當緩沖區(qū)內(nèi)容被讀光后才進行系統(tǒng)調(diào)用將緩沖區(qū)再次填滿。

image

FILE結(jié)構(gòu)體

上面我們看到一個結(jié)構(gòu)體,里面有5個參數(shù),分別記錄了:緩沖區(qū)剩余的字符數(shù)cnt、下一個字符的位置ptr、緩沖區(qū)的位置base、文件訪問模式flag、文件描述符fd。

其中文件描述符就是系統(tǒng)調(diào)用open返回的文件描述符fd,是int類型。ptr與base上面圖中已經(jīng)展示了。cnt是緩沖區(qū)剩余字符數(shù),當cnt為0時,會系統(tǒng)調(diào)用read來填滿緩沖區(qū)。flag為文件訪問模式,記錄了文件打開方式、是否到達文件結(jié)尾等。

結(jié)構(gòu)體的具體定義如下,對應調(diào)用fopen返回的文件指針FILE *fp = fopen(xxx,r):

typedef struct _iobuf{
 int cnt; //緩沖區(qū)剩余字節(jié)數(shù)
 char *base; //緩沖區(qū)地址
 char *ptr; //緩沖區(qū)下一個字符地址
 int fd; //文件描述符
 int flag; //訪問模式
} FILE; //別名,與標準庫一致

結(jié)構(gòu)體中有flag字段,flag字段可以是以下幾種的并集:

enum _flags {
 _READ = 1, 
 _WRITE = 2, 
 _UNBUF = 4, //不進行緩沖
 _EOF = 8, 
 _ERR = 16
};

我們注意到其中有一個字段,標識不進行緩沖,說明此種情況下每一次讀取和輸出都調(diào)用系統(tǒng)函數(shù)。一個例子就是標準錯誤流stderr : 當stderr連接的是終端設備時,寫入一個字符就立即在終端設備顯示。

而stdin和stdout都是帶緩沖的,明確的說是行緩沖。本文不考慮行緩沖,默認都是全緩沖,即緩沖區(qū)滿了才刷新緩沖區(qū)。(詳細可以參考《UNIX環(huán)境高級編程》標準I/O庫章節(jié))。

現(xiàn)在我們可以初始化stdin、stdout與stderr:

FILE _iob[OPEN_MAX] = {
 {0,NULL,NULL,_READ,0},
 {0,NULL,NULL,_WRITE,1},
 {0,NULL,NULL,_WRITE|_UNBUF,2}
};

_ferror/_feof/_fileno

//判斷文件流中是否有錯誤發(fā)生
int _ferror(FILE *f){
 return f-> flag & _ERR;
}
//判斷文件流是否到達文件尾
int _feof(FILE *f){
 return f-> flag & _EOF;
}
//返回文件句柄,即open函數(shù)的返回值
int _fileno(FILE *f){
 return f->fd;
}

_fopen

FILE *_fopen(char *file,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 == 0){ 
 break;
 }
 }
 if(fp >= _iob + OPEN_MAX){
 return NULL;
 }
 if(*mode == 'w'){
 fd = creat(file,PERMS);
 }else if(*mode == 'r'){
 fd = open(file,O_RDONLY,0);
 }else{ //a模式
 if((fd = open(file,O_WRONLY,0)) == -1){
 fd = creat(file,PERMS);
 }
 lseek(fd,0L,2); //文件指針指向末尾
 }
 if(fd == -1){
 return NULL;
 }
 fp->fd = fd;
 fp->cnt = 0; //fopen不分配緩存空間
 fp->base = NULL;
 fp->ptr = NULL;
 fp->flag = *mode == 'r' ? _READ : _WRITE;
 return fp;
}

fopen的處理過程:

判斷打開模式的合法性。

在_iob中尋找一個空閑位置,找不到的話說明程序打開的文件數(shù)已經(jīng)到達的最大值,不能再打開新的文件。

如果是w模式,創(chuàng)建一個新文件。如果是r模式,以只讀方式打開文件。如果是a模式,首先打開文件,如果打開失敗則創(chuàng)建文件,否則通過系統(tǒng)調(diào)用lseek將文件指針置到末尾。

對FILE結(jié)構(gòu)體進行初始化,注意fopen不會分配緩沖區(qū)。

_getc

getc的作用是從文件中返回下一個字符,參數(shù)是文件指針,即FILE:

int _getc(FILE *f){
 return --f->cnt >= 0 ? *f->ptr++ : _fillbuf(f);
}

對照上面的圖示:當緩沖區(qū)中還有剩余字符待讀取時,讀取該字符并返回,并將緩沖區(qū)指針向后移動一個char單位,否則就調(diào)用_fillbuf函數(shù)填滿緩沖區(qū),_fillbuf的返回值就是待讀取的字符。

這里有一個問題:當讀取到最后一個字符時,cnt為0,但是ptr已經(jīng)越界了,如下圖:

image

這種情況雖然是非法的,但是C語言中保證:數(shù)組末尾之后的第一個元素(即&arr[n],或者arr + n)的指針算術運算可以正確執(zhí)行。下面的例子也是上述的一種應用場景:

int a[10] = {1,2,3,4,5,6,7,8,9,10};
for(int *x = a;x < a + 10; x++){
 printf("%d\n",*x);
}

當for循環(huán)到最后一步時,x也指向了a[10],雖然是非法的,但是C語言保證可以正確執(zhí)行,只要不出現(xiàn)如下情況就ok:

int a[10] = {1,2,3,4,5,6,7,8,9,10};
int *x;
for(x = a;x < a + 10; x++){
 printf("%d\n",*x);
}
*x = 11; //越界進行值訪問

_fillbuf

我們看下_getc中的_fillbuf的實現(xiàn),_fillbuf是當緩沖區(qū)沒有可以讀取的字符時,通過系統(tǒng)調(diào)用read讀取一定字節(jié)的數(shù)據(jù)填滿緩沖區(qū),供之后使用:

int _fillbuf(FILE *f){
 int bufsize;
 if((f->flag & (_READ | _EOF | _ERR)) != _READ){ //判斷文件是否可讀
 return EOF;
 }
 bufsize = f->flag & _UNBUF ? 1 : BUFSIZ;
 if(f->base == NULL){  //沒有分配過緩沖區(qū)
 if((f->base = (char *)malloc(bufsize)) == NULL){
  return EOF;
 }
 }
 f->ptr = f->base;
 int n = read(f->fd,f->ptr,BUFSIZ); //系統(tǒng)調(diào)用read
 if(n == 0){  //到達文件結(jié)尾
 f->base = NULL;
 f->cnt = 0;
 f-> flag |= _EOF;
 return EOF;
 }else if(n == -1){ //出錯
  f->cnt= 0;
 f->flag |= _ERR;
  return EOF;
 }else{
 f->cnt = --n;
 return *f->ptr++; 
 }
}

_fillbuf的處理過程:

判斷文件是否可讀

判斷調(diào)用read函數(shù)時應該讀取的字節(jié)數(shù),當文件設置了無緩沖時,讀取1個字節(jié),否則讀取BUFSIZ個字節(jié),BUFSIZ在

判斷是否分配過緩沖區(qū)(fopen不會分配緩沖區(qū),會再第一次調(diào)用getc時分配)。

調(diào)用系統(tǒng)函數(shù)read。

判斷read返回值,分為到達文件結(jié)尾、出錯和正常讀取三種情況。

正常情況下返回緩沖區(qū)第一個字符給getc函數(shù),并將cnt減1。

這里注意,到達文件結(jié)尾和出錯都是返回EOF,區(qū)別是前者會將flag的_EOF位置1,后者會將flag的_ERR位置1,上游可以通過feof和ferror函數(shù)進行判斷(這兩個函數(shù)在上面已經(jīng)實現(xiàn)過了)。

The character read is returned as an int value.
If the End-of-File is reached or a reading error happens, the function returns EOF and the corresponding error or eof indicator is set. You can use either ferror or feof to determine whether an error happened or the End-Of-File was reached.

_putc

int _putc(int x,FILE *f){
 return --f->cnt >= 0 ? *f->ptr++ = x : _flushbuf(x,f);
}

與_getc的實現(xiàn)相似,將寫入的字符放到ptr指向的位置,并將ptr向后移動一位。當緩沖區(qū)滿時,調(diào)用_flushbuf將緩沖區(qū)內(nèi)容刷新到文件中。

_flushbuf

int _flushbuf(int x,FILE *f){
 if((f->flag & (_WRITE | _EOF | _ERR)) != _WRITE){ //判斷文件是否可寫
 return EOF;
 }
 int n;
 int bufsize = f->flag & _UNBUF ? 1 : BUFSIZ;
 if(f->base != NULL){
 n = write(f->fd,f->base,f->ptr - f->base); //判斷需要寫入多少字節(jié)
 if(n != f->ptr - f->base){
  f->flag |= _ERR;
  return EOF;
 }
 }else{
 if((f->base = (char *)malloc(bufsize)) == NULL){
  f->flag |= _ERR;
  return EOF;
 }
 }
 if(x != EOF){
 f->cnt = bufsize - 1;
 f->ptr = f->base;
 *f->ptr++ = x;
 }else{  //當寫入EOF時,代表強制刷新緩沖區(qū)內(nèi)容到文件中
 f->cnt = bufsize;
 f->ptr = f->base;
 }
 return x;
}

_flushbuf的處理過程:

判斷文件是否可寫。

當已分配過緩沖區(qū)時,將緩沖區(qū)的內(nèi)容通過系統(tǒng)調(diào)用write寫入文件中。

當沒有分配過緩沖區(qū)時,分配緩沖區(qū)。

判斷當寫入的字符為EOF時,說明調(diào)用此函數(shù)的目的為強制刷新緩沖區(qū),不寫入字符。將cnt賦值為BUFSIZ,ptr賦值為緩沖區(qū)首地址base。

當寫入字符不為EOF時,說明緩沖區(qū)已滿,需要將緩沖區(qū)刷新到文件中。cnt為BUFSIZE - 1,將寫入的字符x放到到緩沖區(qū)的第一格,然后將ptr向后移動一個char單位。

注意,調(diào)用write函數(shù)時,寫入的字節(jié)數(shù)不能寫死為1或者BUFSIZ:

n = write(f->fd,f->base,f->ptr - f->base); //判斷需要寫入多少字節(jié)

image

如上圖,我們需要寫入base至ptr之間的數(shù)據(jù),而不是BUFSIZ,因為我們可能會強制刷新緩沖區(qū)而不是等到緩沖區(qū)滿了才刷新緩沖區(qū)。

還有一點:當我們想要強制刷新緩沖區(qū)時,第一個參數(shù)x該傳入什么呢?K&R傳遞的是字符0,但是我認為這樣會污染緩沖區(qū),所以我的實現(xiàn)是傳入一個特殊字符EOF,根據(jù)EOF來做不同的處理:

if(x != EOF){
 f->cnt = bufsize - 1;
 f->ptr = f->base;
 *f->ptr++ = x;
}else{  //當寫入EOF時,代表強制刷新緩沖區(qū)內(nèi)容到文件中
 f->cnt = bufsize;
 f->ptr = f->base;
}

當緩沖區(qū)滿時,刷新緩沖區(qū)后緩沖區(qū)的表現(xiàn):

image

當強制刷新緩沖區(qū)時,緩沖區(qū)的表現(xiàn):

image

但是按照K&R的方式來強制刷新緩沖區(qū)時,緩沖區(qū)的表現(xiàn):

image

這樣會污染緩沖區(qū),所以我的實現(xiàn)是傳入EOF來強制刷新緩沖區(qū)。

_fflush

_fflush(FILE *f)的作用是把緩沖區(qū)內(nèi)容寫入文件。當參數(shù)為空時,會刷新所有文件:

int _fflush(FILE *f){
 int res = 0;
 if(f == NULL){
 for(int i = 0; i < OPEN_MAX; i++){  //當參數(shù)為NULL時,刷新所有的文件流
   if((f->flag & _WRITE) && (_fflush(&_iob[i]) == -1)){ //有一個出錯即返回-1
    res = EOF;
   }
 }
 }else{
  if(f->flag & _WRITE){
   _flushbuf(EOF,f);
  }else{
  res = EOF;
  }
 }
 if(f->flag & _ERR){ //出錯
  res = EOF;
 }
 return res;
}

_fflush的處理過程:

判斷參數(shù)是否為空,如果為空的話,遍歷_iob數(shù)組,將所有文件流都強制刷新。

如果參數(shù)不為空,判斷文件是否可寫,再調(diào)用_flushbuf進行刷新,注意此處傳遞給_flushbuf的參數(shù)是EOF。還有一點就是判斷_flushbuf是否出錯不是判斷返回值是否為-1,因為參數(shù)為EOF時的返回值也為-1,所以此處用flag & _ERR判斷是否出錯。

注意,這里我們只針對可寫的文件流進行操作,忽略了只讀的文件流:

If the stream was open for reading, the behavior depends on the specific implementation. In some implementations this causes the input buffer to be cleared.

針對只讀的文件流,不同系統(tǒng)處理的方式不一樣,有的系統(tǒng)會清空緩沖區(qū)。

_fclose

int _fclose(FILE *f){
 int ret;
 if((ret = _fflush(f)) != EOF){
  free(f->base);
  f->base = NULL;
  f->ptr = NULL;
  f->fd = 0;
  f->flag = 0;  
    f->cnt=0;
 }
 return 0;
}

fclose調(diào)用fflush函數(shù),保證在文件關閉前將緩沖區(qū)中的內(nèi)容刷到文件中,并且釋放掉緩沖區(qū)的內(nèi)存空間。

_fseek

關于fseek的介紹請看fseek

int _fseek(FILE *f,long offset,int origin){
 int rc;
 if(f->flag & _READ) {
  if(origin == 1) {
   offset -= f->cnt;
  }
  rc = lseek(f->fd,offset,origin);
    f->cnt = 0;    //將緩沖區(qū)剩余字符數(shù)清0
 }else if(f->flag & _WRITE) {
    rc = _fflush(f);  //強制刷新緩沖區(qū)
  if(rc != EOF) {
   rc = lseek(f->fd,offset,origin);
  }
 }
 return rc == -1 ? EOF : 0;
}
int _fseek(FILE *f,long offset,int origin){
 int rc;
 if(f->flag & _READ) {
  if(origin == 1) {
   offset -= f->cnt;
  }
  rc = lseek(f->fd,offset,origin);
    f->cnt = 0;    //將緩沖區(qū)剩余字符數(shù)清0
 }else if(f->flag & _WRITE) {
    rc = _fflush(f);  //強制刷新緩沖區(qū)
  if(rc != EOF) {
   rc = lseek(f->fd,offset,origin);
  }
 }
 return rc == -1 ? EOF : 0;
}

當文件流為可讀時,見下圖:

image

由于有緩沖區(qū)的存在,我們直覺上的文件指針位置和真實的文件指針位置是不同的,差了cnt個單位長度。所以當我們設置移動offset個長度時,真實的文件指針需要移動offset-cnt個單位長度(offset為正數(shù)或者負數(shù))。

之后我們需要將cnt置為0,以便下次讀取時將緩沖區(qū)的數(shù)據(jù)更新。

當origin為0或者2時,直接調(diào)動lseek即可。

而當文件流為可寫時,見下圖:

image

真實的文件指針位置與我們直覺上的文件指針位置差了ptr - base個單位長度,即我們新寫入緩沖區(qū)的內(nèi)容長度,所以我們直接調(diào)用_fflush即可。(K&R中直接調(diào)用的write,但是我覺得這樣沒有重置ptr指針的位置和cnt,這樣的話base與ptr之間的內(nèi)容會被刷入到文件中兩次)。

當文件是以a模式打開時,fseek無效:

a+  Open for reading and appending (writing at end of file). The file is created if it does not exist. The initial file 
  position for reading is at the beginning of the file, but output is always appended to the end of the file.

_getchar

int _getchar(){
 return _getc(stdin);
}

我們可以發(fā)現(xiàn),_getchar調(diào)用的就是_getc,只不過_getc可以傳入任意的文件指針,而對_getchar來說,_getc傳入的是stdin,也就是{0,NULL,NULL,_READ,0}。

當調(diào)用getchar時,首先去stdin結(jié)構(gòu)體中的緩存取數(shù)據(jù),如果緩存為空,會在_fillbuf中的int n = read(f->fd,f->ptr,BUFSIZ); //系統(tǒng)調(diào)用read處阻塞住,等待用戶輸入字符。

當標準輸入(stdin)連接的是終端時,終端I/O會采用規(guī)范模式輸入處理:對于終端輸入以行為單位進行處理,對于每個讀請求,終端設備輸入隊列會返回一行(用戶輸入的字符會緩存在終端輸入隊列中,直到用戶輸入一個行定界符,輸入隊列中的數(shù)據(jù)會返回給read函數(shù))。

這一行數(shù)據(jù)會緩存在標準I/O緩沖區(qū)中,下次調(diào)用getchar時會返回緩沖區(qū)第一個字符。當緩沖區(qū)數(shù)據(jù)被讀光時,重復上述過程。

_putchar

int _putchar(int x){
 return _putc(x,stdout);
}

我們可以發(fā)現(xiàn),_putchar調(diào)用的就是_putc,只不過_putc可以傳入任意的文件指針,而對_putchar來說,_putc傳入的是stdout,也就是{0,NULL,NULL,_WRITE,1}。

image

調(diào)用putchar時,數(shù)據(jù)會緩存在stdout中的緩沖中。

當stdout的緩沖被裝滿時,會調(diào)用write將數(shù)據(jù)寫入到stdout中,stdout將數(shù)據(jù)寫入到終端設備輸出隊列中,輸出隊列將數(shù)據(jù)寫入到終端。

完整代碼

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#define EOF -1
#define BUFSIZ 1024
#define OPEN_MAX 20  //打開的最大文件數(shù)
#define PERMS 0666
typedef struct _iobuf{
 int cnt;  //緩沖區(qū)剩余字節(jié)數(shù)
 char *base;  //緩沖區(qū)地址
 char *ptr;  //緩沖區(qū)下一個字符地址
 int flag;  //訪問模式
  int fd;   //文件描述符
} FILE;  //別名,與標準庫一致
extern FILE _iob[OPEN_MAX];
//八進制
enum _flags {
 _READ = 01,  
 _WRITE = 02, 
 _UNBUF = 04,  //不進行緩沖
 _EOF = 010,  
 _ERR = 020 
};
FILE _iob[OPEN_MAX] = {
  {0,NULL,NULL,_READ,STDIN_FILENO},
  {0,NULL,NULL,_WRITE,STDOUT_FILENO},
  {0,NULL,NULL,_WRITE|_UNBUF,STDERR_FILENO}
};
#define stdin (&_iob[0])
#define stdout (&_iob[1])
#define stderr (&_iob[2])
int _ferror(FILE *f){
 return f-> flag & _ERR;
}
int _feof(FILE *f){
 return f-> flag & _EOF;
}
int _fileno(FILE *f){
 return f->fd;
}
//返回第一個字符
int _fillbuf(FILE *f){
 int bufsize;
 if((f->flag & (_READ | _EOF | _ERR)) != _READ){  //判斷文件是否可讀
  return EOF;
 }
 bufsize = f->flag & _UNBUF ? 1 : BUFSIZ;
 if(f->base == NULL){   //沒有分配過緩沖區(qū)
  if((f->base = (char *)malloc(bufsize)) == NULL){
   return EOF;
  }
 }
 f->ptr = f->base;
 int n = read(f->fd,f->ptr,BUFSIZ);  //系統(tǒng)調(diào)用read
 if(n == 0){   //到達文件結(jié)尾
  f->base = NULL;
  f->cnt = 0;
  f-> flag |= _EOF;
  return EOF;
 }else if(n == -1){  //出錯
  f->cnt= 0;
  f->flag |= _ERR;
  return EOF;
 }else{
  f->cnt = --n;
  return *f->ptr++; 
 }
}
int _flushbuf(int x,FILE *f){
 if((f->flag & (_WRITE | _EOF | _ERR)) != _WRITE){
  return EOF;
 }
 int n;
 int bufsize = f->flag & _UNBUF ? 1 : BUFSIZ;
 if(f->base != NULL){
  n = write(f->fd,f->base,f->ptr - f->base);  //判斷需要寫入多少字節(jié)
  if(n != f->ptr - f->base){
   f->flag |= _ERR;
   return EOF;
  }
 }else{
  if((f->base = (char *)malloc(bufsize)) == NULL){
   f->flag |= _ERR;
   return EOF;
  }
 }
 if(x != EOF){
  f->cnt = bufsize - 1;
  f->ptr = f->base;
  *f->ptr++ = x;
 }else{   //當寫入EOF時,代表強制刷新緩沖區(qū)內(nèi)容到文件中
  f->cnt = bufsize;
  f->ptr = f->base;
 }
 return x;
}
/**
 * @brief _fflush
 * @param f
 * @return
 */
int _fflush(FILE *f){
  int res = 0;
 if(f == NULL){
    for(int i = 0; i < OPEN_MAX; i++){   //當參數(shù)為NULL時,刷新所有的文件流
      if((f->flag & _WRITE) && (_fflush(&_iob[i]) == -1)){ //有一個出錯即返回-1
       res = EOF;
      }
  }
  }else{
   if(f->flag & _WRITE){
     _flushbuf(EOF,f);
   }else{
    res = EOF;
   }
  }
  if(f->flag & _ERR){  //出錯
    res = EOF;
 }
  return res;
}
int _fclose(FILE *f){
 int ret;
 if((ret = _fflush(f)) != EOF){
  free(f->base);
  f->base = NULL;
  f->ptr = NULL;
  f->fd = 0;
  f->flag = 0;  //@TODO
 }
 return 0;
}
int _fseek(FILE *f,long offset,int origin){
 int rc;
 if(f->flag & _READ) {
  if(origin == 1) {
   offset -= f->cnt;
  }
  rc = lseek(f->fd,offset,origin);
    f->cnt = 0;    //將緩沖區(qū)剩余字符數(shù)清0
 }else if(f->flag & _WRITE) {
    rc = _fflush(f);  //強制刷新緩沖區(qū)
  if(rc != EOF) {
   rc = lseek(f->fd,offset,origin);
  }
 }
 return rc == -1 ? EOF : 0;
}

int _getc(FILE *f){
 return --f->cnt >= 0 ? *f->ptr++ : _fillbuf(f);
}
int _putc(int x,FILE *f){
 return --f->cnt >= 0 ? *f->ptr++ = x : _flushbuf(x,f);
}
int _getchar(){
 return _getc(stdin);
}
int _putchar(int x){
 return _putc(x,stdout);
}
FILE *_fopen(char *file,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 == 0){
   break;
  }
 }
 if(fp >= _iob + OPEN_MAX){
  return NULL;
 }
 if(*mode == 'w'){
  fd = creat(file,PERMS);
 }else if(*mode == 'r'){
  fd = open(file,O_RDONLY,0);
 }else{  //a模式
  if((fd = open(file,O_WRONLY,0)) == -1){
   fd = creat(file,PERMS);
  }
  lseek(fd,0L,2);  //文件指針指向末尾
 }
 if(fd == -1){
  return NULL;
 }
 fp->fd = fd;
 fp->cnt = 0;  //fopen不分配緩存空間
 fp->base = NULL;
 fp->ptr = NULL;
 fp->flag = *mode == 'r' ? _READ : _WRITE;
 return fp;
}
int main(int argc,char *argv[]){

 FILE *f = _fopen("zyc.txt","a"); 
 /*char c;
  for(int i = 0; i < 10; i++){
  c = _getc(f);
  }*/
 /*for(int i = 0; i < 9; i++){
  _putc('6',f);
  }
  _fseek(f,-5,1);
  for(int i = 0; i < 9; i++){
  _putc('8',f);
  }
  _fclose(f);*/
  int c;
  while((c = _getchar()) != '\n'){
   _putchar(c);   
  }
  _fclose(stdout);
 return 0;
}

上面提到的部分函數(shù)在Answer to Exercise 8-3, page 179中有更詳細的實現(xiàn)。

以上這篇C 標準I/O庫的粗略實現(xiàn)教程就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • 詳解Ubuntu18.04配置VSCode+CMake的C++開發(fā)環(huán)境

    詳解Ubuntu18.04配置VSCode+CMake的C++開發(fā)環(huán)境

    這篇文章主要介紹了詳解Ubuntu18.04配置VSCode+CMake的C++開發(fā)環(huán)境,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-03-03
  • C語言實現(xiàn)電話簿管理系統(tǒng)課程設計

    C語言實現(xiàn)電話簿管理系統(tǒng)課程設計

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)電話簿管理系統(tǒng)課程設計,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • C語言可變參數(shù)列表的用法與深度剖析

    C語言可變參數(shù)列表的用法與深度剖析

    這篇文章主要給大家介紹了關于C語言可變參數(shù)列表的相關資料,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2022-02-02
  • C語言動態(tài)內(nèi)存的分配實例詳解

    C語言動態(tài)內(nèi)存的分配實例詳解

    動態(tài)內(nèi)存管理同時還具有一個優(yōu)點,當程序在具有更多內(nèi)存的系統(tǒng)上需要處理更多數(shù)據(jù)時,不需要重寫程序,下面這篇文章主要給大家介紹了關于C語言動態(tài)內(nèi)存分配的相關資料,需要的朋友可以參考下
    2022-06-06
  • C++ 非遞歸實現(xiàn)二叉樹的前中后序遍歷

    C++ 非遞歸實現(xiàn)二叉樹的前中后序遍歷

    本文將結(jié)合動畫和代碼演示如何通過C++ 非遞歸實現(xiàn)二叉樹的前中后序的遍歷,代碼具有一定的價值,感興趣的同學可以學習一下
    2021-11-11
  • C++簡明圖解分析靜態(tài)成員與單例設計模式

    C++簡明圖解分析靜態(tài)成員與單例設計模式

    與靜態(tài)數(shù)據(jù)成員不同,靜態(tài)成員函數(shù)的作用不是為了對象之間的溝通,而是為了能處理靜態(tài)數(shù)據(jù)成員,靜態(tài)成員函數(shù)沒有this指針。既然它沒有指向某一對象,也就無法對一個對象中的非靜態(tài)成員進行默認訪問
    2022-06-06
  • 深入解析Linux下\r\n的問題

    深入解析Linux下\r\n的問題

    本篇文章是對Linux下\r\n的問題進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • C++執(zhí)行shell命令的多種實現(xiàn)方法

    C++執(zhí)行shell命令的多種實現(xiàn)方法

    在linux系統(tǒng)下,用C++程序執(zhí)行shell命令有多種方式,主要介紹了3中方法,具有一定的參考價值,感興趣的可以了解一下
    2021-11-11
  • C/C++細數(shù)宏與函數(shù)有那些區(qū)別

    C/C++細數(shù)宏與函數(shù)有那些區(qū)別

    在C程序中,可以用宏代碼提高執(zhí)行效率。宏代碼本身不是函數(shù),但使用起來象函數(shù)。預處理器用復制宏代碼的方式代替函數(shù)調(diào)用,省去了參數(shù)壓棧、生成匯編語言的CALL調(diào)用、返回參數(shù)、執(zhí)行return等過程,從而提高了速度
    2022-10-10
  • OpenCV實現(xiàn)圖像膨脹

    OpenCV實現(xiàn)圖像膨脹

    這篇文章主要為大家詳細介紹了OpenCV實現(xiàn)圖像膨脹,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-06-06

最新評論