日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

C语言学习第017课——C语言提高(一)

發布時間:2024/1/1 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C语言学习第017课——C语言提高(一) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

typedef的用法

1、定義指針類型

定義兩個char*的變量,p1和p2,使用C++代碼打印一下他倆的類型:

#include <iostream> #include<typeinfo>using namespace std;int test(){char* p1,p2;cout<< typeid(p1).name() <<endl;cout<< typeid(p2).name() <<endl; } int main() {test();return 0; }

運行結果:

這是為什么呢?
定義指針還有一種寫法:

char *p1;

所以也就可以理解為,char修飾的是p1和p2, “*” 修飾的才是指針
如果想讓兩個打印結果都是Pc的話,應該這樣定義:

int test(){char *p1,*p2;cout<< typeid(p1).name() <<endl;cout<< typeid(p2).name() <<endl; }

或者直接用typedef將char* 起一個別名,原理和上面代碼一樣的

#include <iostream> #include<typeinfo>using namespace std;typedef char* PCHAR;int test(){PCHAR p1,p2;cout<< typeid(p1).name() <<endl;cout<< typeid(p2).name() <<endl; }

2、定義特殊類型

例如:在一個平臺上編譯代碼,需要用到long long 類型,而且用到不少這樣的類型,但是在其他平臺編譯代碼,庫里面不支持這個long long類型,只支持int類型,這個時候只需要使用typedef給long long類型起一個別名

typedef long long MYLONG;

在之后需要定義long long類型的數據的時候,直接使用MYLONG定義
如果需要在另一個平臺編譯,只需修改:

typedef int MYLONG;

這一行就OK了

void數據類型的用法

void字面意思是無類型,void* 無類型指針,無類型指針可以指向任何類型的數據,
void定義變量是沒有任何意義的,當你定義void a,編譯器會報錯,因為編譯器不知道分配多少內存給變量。
void真正用在以下兩個方面:

  • 對函數返回的限定
  • 對函數參數的限定

編譯器不知道分配多少內存的情況

上面說到一個使用void定義變量,編譯器報錯,以為不知道分配多少內存
還有一種情況

struct Person{char name[64];int age;struct Person p; };

這種嵌套本身的結構體,編譯器也不知道分配多少內存,因為如果一直往里循環的話,人自己都算不出來要分配多少內存。

有符號數和無符號數的運算和轉換

首先說一下,有符號數和無符號數從表面上來看,是聲明的時候 一個有unsigned一個沒有,但是歸根結底是從二進制來看的
在內存中,所有的數都是以反碼的形式存放的
例如:

int a = 1; 二進制: 00000000 00000000 00000000 00000001 有符號正數,原碼,反碼,補碼都是他本身,最高位0代表是正數 int b = -1; 原碼: 10000000 00000000 00000000 00000001反碼: 11111111 11111111 11111111 11111110補碼: 11111111 11111111 11111111 11111111 有符號負數,原碼最高位1代表負數,剩余數代表數字,反碼,除符號位之外都取反, 補碼:在反碼的基礎上+1 unsigned int c = 1; 二進制: 00000000 00000000 00000000 00000000 無符號數:沒有正負之分,也就不存在符號位,所有的32bit全部表示數值

所以在運算過程中,要從二進制的層面上來考慮

1、int和unsigned int 進行(邏輯)運算

#include <iostream> using namespace std;int test(){int a = -1;unsigned int b = 16;if(a>b){cout<<"負數大于正數了!"<<endl;}return 0; } int main() {test();return 0; }

運行結果:

知識點:有符號數和無符號數進行運算的時候,首先要將有符號數換算成無符號數,運算結果也是無符號數。
也就是說,-1最前面的符號位不按符號算了,算成數字的一部分

-1 補碼: 11111111 11111111 11111111 11111111 16 補碼: 00000000 00000000 00000000 00010000

所以 很明顯,兩個都算成無符號數的話,-1比16大多了,所以會有上面的計算結果
練習:

#include <iostream> using namespace std;int test(){int a = -1;unsigned int b = 16;cout<<a + b<<endl; //15int c = -16;unsigned int d = 15;cout<<c + d<<endl; //4294967295return 0; } int main() {test();return 0; }

分析:a+b 有符號數和無符號數進行運算,先換成無符號數

a=-1 原碼:10000000 00000000 00000000 00000001反碼:11111111 11111111 11111111 11111110補碼:11111111 11111111 11111111 11111111 unsigned int b = 16補碼:00000000 00000000 00000000 00010000 相加結果 00000000 00000000 00000000 00001111 = 15 ---------------------------------------------------------- c = -16 原碼:10000000 00000000 00000000 00010000反碼:11111111 11111111 11111111 11101111補碼:11111111 11111111 11111111 11110000 unsigned int d = 15補碼:00000000 00000000 00000000 00001111 c+d 11111111 11111111 11111111 11111111 = 4294967295(無符號數)

2、unsigned char和char運算

#include <iostream> using namespace std;int test(){char a = -16;unsigned char b = 14;if(a>b){cout<<"負數大于正數了!"<<endl; //沒打印}cout<<a+b<<endl; //-2return 0; } int main() {test();return 0; }

這個運行結果出乎意料,是因為什么呢?
知識點:C語言中比int小的整型(包括short 、unsigned short 、 unsigned char和char)在運算中都要轉換成int然后進行運算,至于為什么選擇轉換為int,應該是從效率上考慮的,因為通常情況下int的長度被定義為機器處理效率最高的長度,比如32位機上,一次處理4個字節效率是最高的,所以雖然short更節省內存,但是在運算中的效率,是int更高。所以上面,無論是邏輯運算a>b還是算術運算a+b中a和b都默認轉換成了int,不是unsigned int!轉換成了int,不是unsigned int!轉換成了int 不是unsigned int!

如果是unsigned的類型轉換成int類型,高位補0.
如果是signed的類型轉換成int類型,如果原來最高位是1則補1,如果是0則補0。
那么上面的結果就解釋的通了:

char a = -16原碼: 10010000反碼: 11101111補碼: 11110000 因為是有符號數,最高位是1,所以變成int類型,前面補1 int a = 11111111 11111111 11111111 11110000unsigned char b = 14補碼: 00001110 因為是無符號數,最高位不管是什么,直接補0 int b = 00000000 00000000 00000000 00001110int類型a為負數,b為正數,所以第一行打印不會出來 a+b = 11111111 11111111 11111111 11111110 這是個負數,要獲取到他的值,需要取反,再+1取反: 10000000 00000000 00000000 00000001+110000000 00000000 00000000 00000010 =-2

練習:

#include <iostream> using namespace std;int test(){char a = -16;unsigned char b = 255;char c = 255;cout<<a+b<<endl;//239cout<<a+c<<endl;//-17return 0; } int main() {test();return 0; }

分析:

char a = -16 換成int類型 11111111 11111111 11111111 11110000 unsigned char b = 255補碼: 11111111 換成int類型(是unsigned類型,所以前面直接補000000000 00000000 00000000 11111111 char c = 255補碼: 11111111 換成int類型(是signed類型,最高位是1,所以前面直接補111111111 11111111 11111111 11111111 ------------------------------------------------------- a+b 00000000 00000000 00000000 11101111 =239 a+c 11111111 11111111 11111111 11101111 負數取反: 10000000 00000000 00000000 00010000+110000000 00000000 00000000 00010001 = -17

局部定義的變量不能當做返回值

#include<stdio.h> #include<stdlib.h> #include<string.h>char* getStr(){char str[] = "hello world!";return str; } int main(void){char* s = NULL;s = getStr();printf("s = %s\n",s); }

以上代碼,一個函數里面定義了一個字符串,然后直接返回了,在主函數中取到這串字符串,然后打印,很簡單,但是運行結果是s=(null),為什么呢?
因為在函數中定義的字符串是保存在棧中的,函數一結束,棧中的內容就被回收了,在main函數中就無法打印出hello world了,
有人說字符串名稱str從一定意義上來說他是一個指針,這樣返回指針也不行嗎?
那我們來說一下這幾行代碼的總體流程:
首先以

char str[] = "hello world!";

這種方式定義的字符串,hello world 是存儲在常量區的,這一行代碼的意思是,從常量區中將“hello world”復制到棧區,然后復制過來的第一個位置的指針賦值給str,函數中返回的str,其實返回的就是這個指針,函數結束,意味著復制過來的hello world被回收,而str對應的指針,也僅僅就是一個地址了,里面的內容已經不存在了。所以打印的時候會出現NULL。

指針的間接賦值

有了上面的例子,來看一下下面的代碼,看一下打印結果是什么

#include<stdio.h> #include<stdlib.h> #include<string.h>void mallocSpace(char* p){p = (char*)malloc(100);memset(p,0,100);memcpy(p,"hello world",100); } int main(void){char* p = NULL;mallocSpace(p);printf("s = %s\n",p); }

運行結果為:s=(null)
看起來代碼沒有什么問題,main中定義一個指針,將指針傳遞到函數中,給指針賦值,在main中再打印指針指向的字符串,也不存在方法出棧回收內存的問題,但是為什么會打印出來這樣的結果呢?
我們來捋一捋程序在內存中的過程:
首先第一行,定義了一個char類型的指針,指向了NULL
第二行,將指針p傳給了函數,進入函數,注意:此時的這個函數中的p和main中的p已經不是同一個p了(不信你打印一下兩個p的地址一樣不一樣)
進入函數,內存會在棧中加載函數體,加載參數p(新加載出來的),而且在函數中操作的p全部都是這個,跟main中的p一點關系都沒有了,所以最后打印出來會是null
為了證明,我自己加了兩個打印:

既然明白了問題出在哪里,解決辦法就是,要讓函數參數穿進去的p,和main中的p一樣了,那就傳p的地址吧,p已經是一個指針了,相應的函數參數應該是一個二級指針。

#include<stdio.h> #include<stdlib.h> #include<string.h>void mallocSpace(char** p){printf("func %p \n",*p);//00000000*p = (char*)malloc(100);printf("func %p \n",*p);//00772BA8memset(*p,0,100);memcpy(*p,"hello world",100); } int main(void){char* p = NULL;printf("main %p \n",p);//00000000mallocSpace(&p);printf("main %p \n",p);//00772BA8printf("s = %s\n",p);//hello world }

const全局變量和局部變量的區別

const全局變量在常量區,不能修改,用指針也不能修改。
const局部變量存放在棧上,不能直接修改,可以使用指針修改。

#include<stdio.h> #include<stdlib.h> #include<string.h>const int a = 100;//const修飾的全局變量,存放在常量區,不能直接或者間接修改int main(void){const int b = 200;//const修飾的局部變量,存放在棧上,不能直接修改,可以間接修改 }

宏函數

#include<stdio.h> #include<stdlib.h> #include<string.h>#define MYADD(x,y) ((x)+(y))int add(int x,int y){return x+y; } int main(void){int a = 10;int b = 20;printf("a + b = %d\n",MYADD(a,b)); }

以上代碼顯示的,add是一個正常的函數,MYADD就是一個宏函數了
說他是宏函數,其實他不是一個真正的函數
add函數才是真正的函數,因為他有返回值類型,參數,函數體
宏函數在一定場景下效率要比函數高
下面我們來分析一下,是在什么場景下,效率怎么個高的:
首先說普通函數調用流程:
假如,我在main中調用add函數計算結果,從main的第一行開始,過程為:

棧中開辟a,賦值為10--> 棧中開辟b,賦值為20--> 遇到了函數--> 返回地址(記錄代碼運行到這里了,等會運行完函數要繼續跳回到這里的下一行),并跳轉進入函數--> 棧中開辟y,并賦值為20--> 棧中開辟x,并賦值為10(此處函數的參數是從右向左陸續壓棧的)--> 運算結果并存儲在寄存器中,返回。 函數結束,x出棧,y出棧(這里具體是誰安排他們出棧,也不一定,后面說)

而宏函數沒有這么復雜,他只是在預處理的時候,進行簡單的替換文本,啥意思呢?我們之前學過編譯四部曲:預處理,編譯,匯編,鏈接。預處理的過程為:不檢查語法,宏定義展開,頭文件展開,就是這一步里面,他會把代碼中的

printf("a + b = %d\n",MYADD(a,b));

直接替換為:

printf("a + b = %d\n",((a)+(b)) );

就是這么簡單。省去了調用函數的時候,在棧里面的一切開銷,整個過程相當于把宏定義的內容搬過來了,增加了代碼量,是典型的空間換時間
所以說,對于頻繁使用,并且短小的函數,我們一般使用宏函數代替,因為宏函數沒有普通函數的開銷(壓棧,跳轉,返回等)

函數的調用慣例

以上,我們了解了一個函數的調用流程,但是存在兩個問題:
一個問題是上面的例子中函數的兩個參數,是哪個先入棧?
二一個問題是,最后函數結束,參數出棧,是誰管他們出棧的,我們說函數的調用者也可以,函數本身自己也可以,那么到底是由什么決定的呢?

其實,函數的調用者和被調用者對函數的調用有著一致的理解,例如,他們雙方都一致的認為函數的參數是按照某個固定的方式壓入棧中,如果不這樣的話函數將無法正確運行。
如果函數的調用方,在傳遞參數時,先壓入a參數,再壓入b參數,而被調用的函數則認為先壓入的b,再壓入a,那么被調用函數在使用ab的值時,就會顛倒。

因此函數的調用方和被調用方對于函數是如何調用的必須有一個明確的約定,只有雙方都遵循一樣的約定,函數才能被正確的調用。這樣的約定被稱為“調用慣例”
,一個調用慣例一般包擴以下幾個方面:

函數參數的傳遞順序和方式

函數的傳遞有很多種方式,最常見的是通過棧傳遞,函數的調用方將參數壓入棧中,函數自己再從棧中將參數取出,對于有多個參數的函數,調用慣例要規定函數調用方將參數壓棧的順序,從左到右,還是從右到左,有些調用慣例還允許使用寄存器傳遞參數,以提高性能

棧的維護方式

在函數將參數壓入棧中之后,函數體會被調用,此后需要將被壓入棧中的參數全部彈出,以使得棧在函數調用前后保持一致,這個彈出的工作可以由函數的調用方來完成,也可以由函數本身來完成
為了在鏈接的時候對調用慣例進行區分,調用慣例要對函數本身的名字進行修飾,不同的調用慣例有不同的名字修飾策略。
事實上,在C語言中,存在著多個調用慣例,而默認的是cdecl,任何一個沒有顯示指定調用慣例的函數都默認是cdecl慣例,比如我們上面對于add函數的聲明,他的完整寫法應該是:

int _cdecl add(int x,int y){return x+y; }

注意:_cdecl不是標準的關鍵字,在不同的編譯器里可能有不同的寫法,例如gcc里就不存在_cdecl這樣的關鍵字,而是使用_attribute_((cdecl))

調用慣例出棧方參數傳遞名字修飾
cdecl函數調用方從右至左參數入棧下劃線+函數名
stdcall函數本身從右至左參數入棧下劃線+函數名+@+參數字節數
fastcall函數本身前兩個參數由寄存器傳遞,其余參數通過堆棧傳遞@+函數名+@+參數的字節數
pascal函數本身從左至右參數入棧較為復雜,詳見相關文檔

C++里面的調用慣例是thiscall

變量傳遞分析


上圖表示在main函數里面調用了A函數,而A函數又調用了B函數,那么,有幾個情況:
main函數在棧區定義了一個變量,函數A和函數B都可以使用
函數A在棧區定義了一個變量,main不可以使用,函數B可以使用
函數B在棧區定義了一個變量,main和函數A都不可以使用
歸納為:在棧中定義的變量,只要還沒有銷毀,就可以被別的函數使用
如果任意一個函數在堆區定義了一個變量,只要沒有free掉,任何函數都可以使用。

內存的存放方向


棧是開口向下的,越往里面放東西,地址是越來越低的

#include<stdio.h> #include<stdlib.h> #include<string.h>int main(void){int a = 10;int b = 20;int c = 30;int d = 40;printf("%p\n",&a);printf("%p\n",&b);printf("%p\n",&c);printf("%p\n",&d); }


知道了棧的存放方向,那么內存的存放方向是什么樣的呢?換句話說,有一個int類型的數0xaabbccdd,在內存中占4個字節,那這4個字節在內存中是按照aa bb cc dd這樣排的呢?還是按照dd cc bb aa這么排的呢?

#include<stdio.h> #include<stdlib.h> #include<string.h>int main(void){int a = 0xaabbccdd;unsigned char* pchar = &a;printf("%x\n",*pchar); //ddprintf("%x\n",*(pchar+1)); //ccprintf("%x\n",*(pchar+2)); //bbprintf("%x\n",*(pchar+3)); //aa }

指針加1之后,說明內存是往高走的,打印結果也是慢慢往高位打印
這種存放方式叫做小端模式:高位字節存放在高地址,低位字節存放在低地址,
不是所有的系統存放數據都是小端模式,這里做一個了解。
上面的代碼在寫的時候,遇到一個問題,定義pchar的時候,如果使用 char* 打印結果就是:

使用unsigned char*定義pchar的時候,結果為:


原因單獨說,printf使用%x占位符打印signed char結果占4個字節?打印unsigned char結果占1個字節?

野指針

野指針指向一個已經刪除的對象,或未申請訪問受限內存區域的指針,與空指針不同,野指針無法通過簡單的判斷是否為NULL避免,只能通過良好的編程習慣來盡力減少,

什么情況下會導致野指針?

  • 指針變量未初始化
    任何指針變量剛被創建時不會自動成為NULL指針,他的缺省值是隨機的,會亂指一氣,所以指針變量在創建的時候應該初始化,
  • 指針釋放后未置空
    有時指針在free或delete后,未置為NULL,便會使人以為是合法的,其實free尤其是delete,他們只是把指針所指的內存釋放掉,但并沒有把指針本身干掉,此時指針指向的就是垃圾內存,釋放后的指針應當立即置位NULL,防止野指針的出現
  • 指針操作超越變量作用域
    不要返回指向棧內存的指針或引用,因為棧內存在函數結束時會被釋放

指針的步長

看一個例子:

#include<stdio.h> #include<stdlib.h> #include<string.h>int main(void){int a = 100;char buf[64] = {0};memcpy(buf+1,&a,sizeof(int)); //不存放在第一個字節,從第二個字節開始存//取的時候怎么取呢?char* p = &buf;printf("%d\n",*(int*)(p+1));//指針+1,強轉成int類型的指針,再取值 }

所以存的時候,怎么存的,要記住,取得時候要知道從哪里開始取
那么,如果情況復雜一點呢?
如果有一個結構體:

struct Person{int a;char b;char c[64];int d; };

該怎么快速的知道我想要的字段偏移量是多少呢?
有一個函數,需要導一個頭文件

#include<stddef.h> struct Person p = {1,'a',"hello",100}; printf("b offset = %d\n",offsetof(struct Person,b));

函數參數傳一個結構體,和一個字段名稱,就可以返回字段名稱的偏移量,
運行結果:

指針的間接賦值

通過地址強轉為指針再賦值

#include<stdio.h> #include<stdlib.h> #include<string.h>int main(void){int a = 10;printf("%p\n",&a);//28FF3C打印一個int類型的a的地址*(int*)0x28FF3C = 100; int類型的數,地址也是4個字節int類型,前面加上int*他就變成了一個指針,再前面加*取值,再賦值,就可以改變a的值printf("%d\n",a); }

通過修改指針的值

#include<stdio.h> #include<stdlib.h> #include<string.h> void changePoint(int** val){ 函數使用二級指針接著*val = 0x8; 一級指針改值 } int main(void){int* p = NULL;printf("%x\n",p); 打印p的地址為0changePoint(&p); 將指針再次取地址,升級為二級指針,傳進函數printf("%x\n",p); 打印地址 結果為8 }

指針做函數參數的輸入特性

主調函數分配內存,被調函數使用內存
在堆上分配內存

#include<stdio.h> #include<stdlib.h> #include<string.h> void printStr(const char* str){ 參數用一個一級指針接著printf("%s\n",str); 直接打印即可 } int main(void){char* str = (char*)malloc(sizeof(char)*100); 堆內存開辟空間memset(str,0,100); 初始化strcpy(str,"nihao shijie"); 賦值printStr(str); 打印,傳入指針變量 }

在棧上分配內存

#include<stdio.h> #include<stdlib.h> #include<string.h> void printArray(int* arr,int len){ 數組傳入參數,會退化為指針,指向第一個元素的位置for(int i = 0;i<len;i++){printf("%d\n",arr[i]);} } int main(void){int arr[] = {1,2,3,4,5}; 棧中定義一個數組int len = sizeof(arr)/sizeof(arr[0]); 計算出數組長度printArray(arr,len); 將數組和長度傳入函數中 }

下面寫一個棧內存存放字符串的

#include<stdio.h> #include<stdlib.h> #include<string.h> void printStrings(char** strs,int len){ 我們說,普通類型的數組名當做函數參數,會退化為指針上一個例子中,int類型的數組,在函數參數中,要用int*來接著所以在這里,char*類型的數組,在函數參數中要用char**來接著,也就是多一層指針,for(int i = 0;i<len;i++){printf("%s\n",strs[i]);} } int main(void){char* strs[] = { 定義一個char*類型的數組,里面的每一個元素都是char*類型"aaaaa",//這些字母都是存儲在常量區"bbbbb","ccccc","ddddd","eeeee"};int len = sizeof(strs)/sizeof(strs[0]); 計算長度printStrings(strs,len); 傳參 }

指針做函數參數的輸出特性

被調函數分配內存,主調函數使用內存
代碼示例:

#include<stdio.h> #include<stdlib.h> #include<string.h> void mallocSpace(char** temp){ 因為p是指針,傳參的時候又取地址了,所以這里用二級指針接著char*p = (char*)malloc(sizeof(char)*100); 重新定義一個指針給分配內存memset(p,0,100); 初始化strcpy(p,"nihao shijie"); 賦值*temp = p; 指針重定向 } int main(void){char* p = NULL; 定義一個指針p指向空mallocSpace(&p); 把p的地址給了函數,剩下的交給函數printf("%s\n",p); 重新打印一下p的內容if(p!=NULL){free(p); 最后回收這塊內存p = NULL;} }

總結

以上是生活随笔為你收集整理的C语言学习第017课——C语言提高(一)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: av男人在线 | 一级空姐毛片 | 日b免费视频 | 精品丰满人妻无套内射 | 国产九九在线 | 亚洲精品国产熟女久久久 | 一级少妇精品久久久久久久 | 国产理论视频 | 五月天在线 | 亚洲国产精品自拍 | 欧洲一区在线 | 国产精品久久网 | 毛片链接 | 日本精品三区 | 777av| 成年人免费网站在线观看 | 不卡一区二区在线观看 | 日本欧美国产 | 亚洲精品一区 | xxxx日韩| 欧美大片黄 | 欧美精品videos另类日本 | 国产精品乱轮 | 欧美抠逼视频 | 99在线精品视频免费观看20 | 日本欧美视频 | 免费在线观看黄色 | 黄色网络在线观看 | 欧美激情影音先锋 | 亚洲www啪成人一区二区麻豆 | 精品伦理一区二区 | 精品盗摄一区二区三区 | 青青草久| 一区在线免费观看 | 国产成人精品在线视频 | 久久久久国产精品夜夜夜夜夜 | 久久久久久成人精品 | 天天搞天天搞 | 成人刺激视频 | 免费av电影网站 | 久热精品视频在线播放 | 亚洲国产激情 | 日本韩国欧美一区 | 青青国产精品视频 | 五月婷婷激情综合 | 久久久久久久极品内射 | av中文天堂在线 | 精品国产一区二区三区四区精华 | 亚洲精品视频在线播放 | 中文字幕永久在线观看 | 成人精品在线视频 | 亚洲女人天堂色在线7777 | 国产激情视频一区二区 | 色婷婷av一区二区三区四区 | 欧美亚洲国产日韩 | 涩涩视频网站在线观看 | 一级片视频免费观看 | 欧美日韩高清一区二区三区 | 四虎伊人 | 国产精品厕所 | 成人av综合网| 国产精品无码av在线有声小说 | 成人激情电影在线观看 | 国产最新精品视频 | 久久精品综合 | 久久99色 | 亚洲色图28p | 18禁一区二区| 国产精品男同 | 精品国产乱码久久久久久108 | 91爱爱爱爱 | 久久久久久久久国产精品 | 销魂奶水汁系列小说 | 欧美整片在线观看 | 亚洲一区中文字幕永久在线 | 国产1区2区3区4区 | 亚洲国产成人精品久久久 | 午夜综合网 | 91精品婷婷国产综合久久蝌蚪 | 成人在线你懂的 | 欧美日韩18 | 精品美女在线 | av高清一区二区 | 综合另类 | 婷婷综合五月天 | 午夜看片福利 | 天堂网中文在线 | 欧美日韩黑人 | 成人黄色av网址 | 国产激情一区二区三区 | 无码国产69精品久久久久同性 | 黄网在线看| 亚洲日本中文 | 亚洲视频色图 | 国产九九久久 | 欧美在线一级 | 国产精品美女久久久久av超清 | 久久公开视频 | 亚洲涩情 |