亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C++ > 正文

C 標準I/O庫的粗略實現教程

2020-05-23 13:32:01
字體:
來源:轉載
供稿:網友

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

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

fopen與open之間的關系

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

C,標準I/O庫

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

C,標準I/O庫

FILE結構體

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

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

結構體的具體定義如下,對應調用fopen返回的文件指針FILE *fp = fopen(xxx,r):

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

結構體中有flag字段,flag字段可以是以下幾種的并集:

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

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

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

現在我們可以初始化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

//判斷文件流中是否有錯誤發生int _ferror(FILE *f){ return f-> flag & _ERR;}//判斷文件流是否到達文件尾int _feof(FILE *f){ return f-> flag & _EOF;}//返回文件句柄,即open函數的返回值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中尋找一個空閑位置,找不到的話說明程序打開的文件數已經到達的最大值,不能再打開新的文件。

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

對FILE結構體進行初始化,注意fopen不會分配緩沖區。

_getc

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

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

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

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

C,標準I/O庫

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

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循環到最后一步時,x也指向了a[10],雖然是非法的,但是C語言保證可以正確執行,只要不出現如下情況就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的實現,_fillbuf是當緩沖區沒有可以讀取的字符時,通過系統調用read讀取一定字節的數據填滿緩沖區,供之后使用:

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){  //沒有分配過緩沖區 if((f->base = (char *)malloc(bufsize)) == NULL){  return EOF; } } f->ptr = f->base; int n = read(f->fd,f->ptr,BUFSIZ); //系統調用read if(n == 0){  //到達文件結尾 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的處理過程:

判斷文件是否可讀

判斷調用read函數時應該讀取的字節數,當文件設置了無緩沖時,讀取1個字節,否則讀取BUFSIZ個字節,BUFSIZ在

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

調用系統函數read。

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

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

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

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的實現相似,將寫入的字符放到ptr指向的位置,并將ptr向后移動一位。當緩沖區滿時,調用_flushbuf將緩沖區內容刷新到文件中。

_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); //判斷需要寫入多少字節 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時,代表強制刷新緩沖區內容到文件中 f->cnt = bufsize; f->ptr = f->base; } return x;}

_flushbuf的處理過程:

判斷文件是否可寫。

當已分配過緩沖區時,將緩沖區的內容通過系統調用write寫入文件中。

當沒有分配過緩沖區時,分配緩沖區。

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

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

注意,調用write函數時,寫入的字節數不能寫死為1或者BUFSIZ:

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

C,標準I/O庫

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

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

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

當緩沖區滿時,刷新緩沖區后緩沖區的表現:

C,標準I/O庫

當強制刷新緩沖區時,緩沖區的表現:

C,標準I/O庫

但是按照K&R的方式來強制刷新緩沖區時,緩沖區的表現:

C,標準I/O庫

這樣會污染緩沖區,所以我的實現是傳入EOF來強制刷新緩沖區。

_fflush

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

int _fflush(FILE *f){ int res = 0; if(f == NULL){ for(int i = 0; i < OPEN_MAX; i++){  //當參數為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的處理過程:

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

如果參數不為空,判斷文件是否可寫,再調用_flushbuf進行刷新,注意此處傳遞給_flushbuf的參數是EOF。還有一點就是判斷_flushbuf是否出錯不是判斷返回值是否為-1,因為參數為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.

針對只讀的文件流,不同系統處理的方式不一樣,有的系統會清空緩沖區。

_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調用fflush函數,保證在文件關閉前將緩沖區中的內容刷到文件中,并且釋放掉緩沖區的內存空間。

_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;    //將緩沖區剩余字符數清0 }else if(f->flag & _WRITE) {    rc = _fflush(f);  //強制刷新緩沖區  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;    //將緩沖區剩余字符數清0 }else if(f->flag & _WRITE) {    rc = _fflush(f);  //強制刷新緩沖區  if(rc != EOF) {   rc = lseek(f->fd,offset,origin);  } } return rc == -1 ? EOF : 0;}

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

C,標準I/O庫

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

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

當origin為0或者2時,直接調動lseek即可。

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

C,標準I/O庫

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

當文件是以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);}

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

C,標準I/O庫

當調用getchar時,首先去stdin結構體中的緩存取數據,如果緩存為空,會在_fillbuf中的int n = read(f->fd,f->ptr,BUFSIZ); //系統調用read處阻塞住,等待用戶輸入字符。

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

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

_putchar

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

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

C,標準I/O庫

調用putchar時,數據會緩存在stdout中的緩沖中。

當stdout的緩沖被裝滿時,會調用write將數據寫入到stdout中,stdout將數據寫入到終端設備輸出隊列中,輸出隊列將數據寫入到終端。

完整代碼

#include <stdlib.h>#include <unistd.h>#include <fcntl.h>#define EOF -1#define BUFSIZ 1024#define OPEN_MAX 20  //打開的最大文件數#define PERMS 0666typedef struct _iobuf{ int cnt;  //緩沖區剩余字節數 char *base;  //緩沖區地址 char *ptr;  //緩沖區下一個字符地址 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){   //沒有分配過緩沖區  if((f->base = (char *)malloc(bufsize)) == NULL){   return EOF;  } } f->ptr = f->base; int n = read(f->fd,f->ptr,BUFSIZ);  //系統調用read if(n == 0){   //到達文件結尾  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);  //判斷需要寫入多少字節  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時,代表強制刷新緩沖區內容到文件中  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++){   //當參數為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;    //將緩沖區剩余字符數清0 }else if(f->flag & _WRITE) {    rc = _fflush(f);  //強制刷新緩沖區  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;}

上面提到的部分函數在Answer to Exercise 8-3, page 179中有更詳細的實現。

以上這篇C 標準I/O庫的粗略實現教程就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持VEVB武林網。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
深夜精品寂寞黄网站在线观看| 草民午夜欧美限制a级福利片| 国产精品视频xxx| 日韩在线视频免费观看高清中文| 97视频在线观看免费| 欧美性猛交xxxxx免费看| 国产亚洲免费的视频看| 68精品国产免费久久久久久婷婷| 中文字幕亚洲欧美日韩在线不卡| 久久久久亚洲精品| 高跟丝袜欧美一区| 国产91久久婷婷一区二区| 久久伊人色综合| 久久精品国产99国产精品澳门| 性金发美女69hd大尺寸| 日韩欧美国产视频| 日韩在线观看网址| 亚洲人成电影在线播放| 免费91在线视频| 一本色道久久88亚洲综合88| 色中色综合影院手机版在线观看| 亚洲美女黄色片| 亚洲精品久久久久国产| 国产成人精彩在线视频九色| 国产在线视频91| 欧美一级黑人aaaaaaa做受| 精品一区二区三区三区| 日韩中文字幕在线视频播放| 亚洲美女av网站| 国产精品免费久久久久影院| 久久久久久久久国产| 欧美国产视频一区二区| 亚洲免费小视频| 国产精品久久久久久久久久小说| 最近2019中文字幕mv免费看| 国产视频欧美视频| 黑人极品videos精品欧美裸| 久久综合88中文色鬼| 久久久久久噜噜噜久久久精品| 日韩欧美精品免费在线| 国产成人aa精品一区在线播放| 日韩免费在线免费观看| 欧美大片va欧美在线播放| 精品久久久久久国产| 日韩精品免费在线视频| 久久综合伊人77777尤物| 亚洲一区二区少妇| 亚洲区在线播放| 亚洲自拍偷拍色图| 日韩欧美主播在线| 欧美精品videosex性欧美| 91av视频在线免费观看| 日韩一区av在线| 亚洲国产精品视频在线观看| 成人夜晚看av| 亚洲国产精品人久久电影| 国产精品久久久久久av福利软件| 亚洲第一网中文字幕| 国产精品国产三级国产专播精品人| 欧美性受xxxx黑人猛交| 欧美高清不卡在线| 亚洲aa在线观看| 国产精品久久久久久av福利软件| 日韩欧美亚洲国产一区| 日韩暖暖在线视频| 高清一区二区三区四区五区| 亚洲国内精品视频| 国产在线观看91精品一区| 日韩精品在线观看网站| 欧美电影在线观看高清| 成人亚洲激情网| 欧美在线视频一区| 一夜七次郎国产精品亚洲| 国产一区二区三区三区在线观看| 久久久精品中文字幕| 欧美人与性动交a欧美精品| 亚洲国产中文字幕久久网| 亚洲精品一区二区在线| 精品欧美国产一区二区三区| 亚洲午夜av电影| 综合网中文字幕| 色综合久久88色综合天天看泰| 欧美裸体男粗大视频在线观看| 不卡毛片在线看| 一区二区在线视频| 精品国产一区二区三区久久狼5月| 91精品国产91久久久久| 亚洲跨种族黑人xxx| 精品国模在线视频| 日韩在线观看免费网站| 欧美黑人性生活视频| 欧美黄色成人网| 日韩免费中文字幕| 久久99热这里只有精品国产| 久久精品国产亚洲7777| 欧美亚洲激情视频| 夜夜躁日日躁狠狠久久88av| 精品成人乱色一区二区| 国产精品视频网址| 懂色av一区二区三区| 91免费看片在线| xvideos亚洲人网站| 欧美一级高清免费播放| 欧美亚洲视频在线看网址| 欧美精品在线极品| 日韩大胆人体377p| 中文字幕v亚洲ⅴv天堂| 日韩av免费在线观看| 亚洲人线精品午夜| 人九九综合九九宗合| 粉嫩老牛aⅴ一区二区三区| 久热精品视频在线免费观看| 亚洲人a成www在线影院| 日韩av快播网址| 亚洲系列中文字幕| 自拍视频国产精品| 欧洲美女免费图片一区| 日韩成人中文字幕| 九色91av视频| 国产91精品久| 国产精品你懂得| 日本国产高清不卡| 91精品久久久久久久久久入口| 亚洲欧美另类自拍| 久久人人97超碰精品888| 黄色精品在线看| 亚洲精品国精品久久99热| 国产成人免费av| 免费99精品国产自在在线| 午夜精品久久久99热福利| 岛国av在线不卡| 久久香蕉频线观| 亚洲综合成人婷婷小说| 亚洲国产高潮在线观看| 91夜夜揉人人捏人人添红杏| 欧美激情精品久久久久| 中文字幕亚洲国产| 亚洲视频电影图片偷拍一区| 久久精品视频在线播放| 国产精品6699| 亚洲精品电影网| 人九九综合九九宗合| 91色视频在线观看| 日韩高清人体午夜| 蜜臀久久99精品久久久久久宅男| 日韩精品视频在线观看免费| 亚洲一区二区久久久久久久| 国产精品高潮呻吟视频| 欧美一级bbbbb性bbbb喷潮片| 国产日韩av高清| 操日韩av在线电影| 欧美一区亚洲一区| 国产日韩欧美在线播放| 久久中文字幕国产| 欧美成人三级视频网站| 成人有码在线视频| 欧美午夜性色大片在线观看| 国产亚洲美女久久| 成人春色激情网| 国产精品精品视频| 97国产精品人人爽人人做| 欧美日韩国产中字| 欧美一级淫片aaaaaaa视频| 91久久精品日日躁夜夜躁国产|