亚洲欧美第一页_禁久久精品乱码_粉嫩av一区二区三区免费野_久草精品视频

蟲蟲首頁| 資源下載| 資源專輯| 精品軟件
登錄| 注冊

您現在的位置是:首頁 > 技術閱讀 >  【為宏正名】什么?我忘了去上“數學必修課”!

【為宏正名】什么?我忘了去上“數學必修課”!

時間:2024-02-13





【說在前面的話】


在前面的文章《【為宏正名】本應寫入教科書的“世界設定”》中我們了解到:宏會在預編譯階段被“處理掉”——宏會被逐級展開、其最終代表的字符串會被替換到對應的文本文件中(只不過通常這個文本文件就是".c"文件)——它不僅活不到正式的編譯(make)階段,更無法對程序運行時刻的行為產生絲毫影響。
簡而言之,通過宏所確定的內容是在編譯時刻就固化下來的。很多人都了解這一點,也很擅長使用宏的方式來固化一些常數,比如,教科書中最常見的一個例子是:
//! 非閏年的情況下,一年中有多少秒#define SEC_IN_A_YEAR    (60ul * 60ul * 24ul * 365ul)
static uint32_t s_wTotalSecInAYear = SEC_IN_A_YEAR;

例子雖然簡單,但立馬引出了一個有趣的問題:宏展開后,make時編譯器看到的究竟是上述常量表達式的計算結果:

static uint32_t s_wTotalSecInAYear = 31536000ul;

還是原樣的字符串替換呢?

static uint32_t s_wTotalSecInAYear = (60ul * 60ul * 24ul * 365ul);

感興趣的讀者可以通過“-E”來研究一下:

SET PATH=C:\Keil_v5\ARM\ARMCLANG\Bin;armclang -xc -std=gnu11 --target=arm-arm-none-eabi -mcpu=cortex-m4 -E -o "preprocessed_main.c" "main.c"

這里,命令行使用 armclang(Arm Compiler 6)對 “main.c”進行預編譯("-E"的結果),并將結果輸出到一個名為“preprocessed_main.c” 的文件中——而這一文件就是我們在后面文章中要經常觀察的,比如,針對前面的例子,一個可能的輸出結果是:

# 1 "main.c"# 1 "<built-in>" 1# 1 "<built-in>" 3# 370 "<built-in>" 3# 1 "<command line>" 1# 1 "<built-in>" 2# 1 "main.c" 2# 24 "main.c"static uint32_t s_wTotalSecInAYear = (60ul * 60ul * 24ul * 365ul);


【數位拼接律】


如果你認為“預編譯器完全沒有數值計算能力、或是對常量計算漠不關心”,那你就大錯特錯了——體現在宏身上,預編譯器有一種根據需要自動在字符串和數值之間進行轉換的能力。舉個例子:

定義三個獨立的宏,分別代表三個獨立的“數字”:
#define NUM_A       2#define NUM_B       5#define NUM_C       5

借助上一篇文章中引入的膠水宏 CONNECT3()

#define __CONNECT3(__A, __B, __C)       __A##__B##__C#define CONNECT3(__A, __B, __C)         __CONNECT3(__A, __B, __C)

我們可以把這三個宏粘貼在一起:

#define NUM_COMBINE     CONNECT3(NUM_A,NUM_B,NUM_C)

我們當然知道,最終宏替換的結果肯定是字符串“255”,但這個拼接出來的字符串“255”,和十進制數字255是等效的么?換句話說,預編譯器懂得這個字符串“255”的含義么?為了驗證這一問題,我們不妨使用下面的代碼,去直接問問預編譯器本人的看法:

#if NUM_COMBINE > 254#warning larger than 254#endif
#if NUM_COMBINE < 256#warning smaller than 256#endif
#if NUM_COMBINE == 0xFF#warning equals to 0xFF#endif

在 “main.c” 中加入上述全部宏定義以后,進行預編譯,我們會得到如下的結果:



驚呆了!拼接出來的字符串不僅被正確的當作十進制數字256使用,還可以與十六進制數字進行正確的比較!!

這是不是意味著:無論是十進制、十六進制,我們只要想辦法得到對應的“數位”,就可以通過拼接的方法還原出所需進制的“常熟字符串”,而且與編譯器還懂得這一字符串的數學意義!——沒錯,這就是上一篇文章的最后,我們能夠1)把任意通過宏編寫的常量表達式計算出結果,并2)將數值轉換成十進制字符串的原理——恍然大悟的同學可以“單擊這里去重溫一下”,這里就不再贅述了。


【序號自增】


定義和使用宏的時候,我們也許會突發奇想,能不能讓宏里使用的數字實現“序號自增”的效果呢?要回答這個問題,我們不妨根據目前學過的知識簡單推理一下:

  • 預編譯器能夠理解“數字字符串”的數值意義;

  • 宏的本質是一個對目標字符串的引用;

  • 目標字符串是個常量,修改常量是不可能的;

推論:

  • 假設一個宏表示一個序號

  • 我們可以根據當前宏的值,計算出下一個序號的值,并借助“數位拼接律”生成一個新的字符串

  • 修改宏的引用關系,讓它指向新生成的字符串


根據上篇文章中引入的腳本頭文件"mf_u8_dec2str.h",我們可以實現上述效果:

//! 一個用于表示序號的宏,初值是0#define MY_INDEX      0

每次使用下面的預編譯代碼,我們就可以實現將 MY_INDEX的值加一的效果:

//! MFUNC_IN_U8_DEC_VALUE = MY_INDEX + 1; 給腳本提供輸入#define MFUNC_IN_U8_DEC_VALUE    (MY_INDEX + 1)
//! 讓預編譯器執行腳本#include "mf_u8_dec2str.h"
//! MY_INDEX = MFUNC_OUT_DEC_STR; 獲得腳本輸出#define MY_INDEX MFUNC_OUT_DEC_STR

可以看到,雖然原理上可行,如果真用這種方法寫代碼,別說可讀性差到爹媽都不認識,就算大家都能看懂,使用起來實在特別麻煩!否決!


那有沒有一種簡單的方法呢?答案是肯定的——GCC擴展的預編譯語法提供了一個專門的宏,叫做 __COUNTER__——真可謂踏破鐵鞋無覓處,驀然回首,他就在燈火闌珊處——對每個編譯的目標文件來說,__COUNTER__的初值是0,每使用一次,自動加一__COUNTER__是一柄神器,為了顯示它的威力,我們不妨看一個例子:

假設我們要構建一個單向鏈表,它的元素結構如下:
typedef struct node_item_t node_item_tstruct node_item_t {    node_item_t *ptNext;                //!< 指下一個元素    //! 鏈表節點的其它成員    uint8_t chID;                       //!< 假設有一個元素是序號    ...};

實際使用的時候,無論運行時刻鏈表的內容和結構是否會發生變化,但在編譯時刻,我們會給他一些指定數量的初始的節點(比如16個),用數組來存儲:

static node_item_t s_tItemPool[16];static node_item_t *s_ptListRoot = NULL;

一般來說,我們需要編寫一個初始化函數——在運行時刻將 s_tItemPool 中的元素一個一個手工加入到鏈表中(添加到 s_ptListRoot 指向的鏈表中)——這里的代價是雙份的:

  • 初始化函數所占用的代碼空間

  • 添加節點的運行時間。 


借助__COUNTER__我們可以直接在編譯時刻,以數組初始值的形式完成鏈表的初始化

#define ADD_ITEM_TO(__LIST_ADDR, ...)                 \    {                                                 \        .ptNext = &((__LIST_ADDR)[(__COUNTER__ + 1]), \        __VA_ARGS__                                   \    }#define ADD_FINAL_ITEM(...)                           \    {                                                 \        .ptNext = NULL,                               \        __VA_ARGS__                                   \    }

借助這個宏,我們可以實現對鏈表的靜態初始化:

static node_item_t s_tItemPool[] = {    ADD_ITEM_TO(s_tItemPool),          //!< 添加節點0    ADD_ITEM_TO(s_tItemPool),          //!< 添加節點1    ...    ADD_ITEM_TO(s_tItemPool),          //!< 添加節點n-1    ADD_FINAL_ITEM(s_tItemPool),       //!< 添加最后一個節點};static node_item_t *s_ptListRoot = s_tItemPool;

注意到節點內還有一個節點的序號“chID”,我們其實也可以一并將其自動初始化了——當然要記住,每次使用__COUNTER__它的值都會增加1——修改宏如下:

#define ADD_ITEM_TO(__LIST_ADDR, ...)                   \    {                                                   \        .ptNext = &((__LIST_ADDR)[(__COUNTER__/2 + 1]), \        .chID = (__COUNTER__ / 2),                      \        __VA_ARGS__                                     \    }#define ADD_FINAL_ITEM(__LIST_ADDR, ...)                \    {                                                   \        .ptNext = NULL,                                 \        .chID = (__COUNTER__ / 2),                      \        __VA_ARGS__                                     \    }

修改后,實際展開效果如下:

static node_item_t s_tItemPool[] = {    { .ptNext = &((s_tItemPool)[(0/2 + 1]), .chID = (1 / 2), },    { .ptNext = &((s_tItemPool)[(2/2 + 1]), .chID = (3 / 2), },    ...    { .ptNext = &((s_tItemPool)[(4/2 + 1]), .chID = (5 / 2), },    { .ptNext = NULL, .chID = (6 / 2), },};static node_item_t *s_ptListRoot = s_tItemPool;


上述效果雖然看似令人滿意,但存在一個巨大的隱患,而這一隱患同樣來自于__COUNTER__宏的基本特性:每次使用__COUNTER__它的值都會增加1——換句話說,在你使用 ADD_ITEM_TO() 的時候,如何才能確保 __COUNTER__是從0開始編號的呢?——別的宏可能已經使用過它了。


要解決這一問題,我們就不得不借助宏的“好基友”——枚舉的幫助了。基本思路是這樣的:
  • 無論 __COUNTER__ 是什么值,我們都可以將其傳遞給一個枚舉——作為初始值;

  • 使用 __COUNTER__ 時,我們首先通過枚舉將初始值扣除,從而獲得“從0開始的計數”


說干就干:
#define __LIST_ROOT(__NAME)    s_ptList##__NAME##Root#define LIST_ROOT(__NAME)      __LIST_ROOT(__NAME)
#define __IMP_LIST(__NAME)                               \ enum { \        /* 這里 "+1" 是把本次使用__COUNTER__也算進去 */        \ list_##__NAME##_start = __COUNTER__ + 1, \ }; \    static node_item_t s_tList##__NAME##Pool[] = {               #define __END_IMP_LIST(__NAME) \ }; \ static node_item_t *LIST_ROOT(__NAME) = \ s_tList##__NAME##Pool; #define IMP_LIST(__NAME, ...) \            __IMP_LIST(__NAME, __VA_ARGS__)    #define END_IMP_LIST(__NAME) __END_IMP_LIST(__NAME)
#define __ADD_ITEM_TO(__NAME, ...) \ { \ .ptNext = &(s_tList##__NAME##Pool[ \ (__COUNTER__ - list_##__NAME##_start)/2 + 1]), \ .chID = ((__COUNTER__ - list_##__NAME##_start) / 2), \ __VA_ARGS__ \ } #define ADD_ITEM_TO(__NAME, ...) \ __ADD_ITEM_TO(__NAME, __VA_ARGS__) #define __ADD_FINAL_ITEM(__NAME, ...) \ { \ .ptNext = NULL, \ .chID = ((__COUNTER__ - list_##__NAME##_start) / 2), \ __VA_ARGS__ \ }#define ADD_FINAL_ITEM(__NAME, ...) \ __ADD_FINAL_ITEM(__NAME, __VA_ARGS__)

為了方便隱藏定義枚舉的“小動作”,我們追加了一對宏 IMP_LIST()END_IMP_LIST(),就是"implement list"的縮寫,它實現了以下功能:

  • 以指定的名字定義了一個枚舉;

  • 以指定的名字定義了鏈表的節點池;

  • 以指定的名字定義了指向鏈表的根指針,用戶可以通過宏LIST_ROOT()來獲取這一指針;


修改應用代碼,實現一個叫做 MyList 的鏈表:

//! 實現一個list,名字叫 MyListIMP_LIST(MyList)
ADD_ITEM_TO(MyList), //!< 添加節點0 ADD_ITEM_TO(MyList), //!< 添加節點1 ... ADD_ITEM_TO(MyList), //!< 添加節點n-1 ADD_FINAL_ITEM(MyList), //!< 添加最后一個節點 END_IMP_LIST(MyList)

是不是看起來很“優雅”?實際展開效果如下:

enum { list_MyList_start = 0 + 1, }; static node_item_t s_tListMyListPool[] = {
{ .ptNext = &(s_tListMyListPool[ (1 - list_MyList_start)/2 + 1]), .chID = ((2 - list_MyList_start) / 2), }, { .ptNext = &(s_tListMyListPool[ (3 - list_MyList_start)/2 + 1]), .chID = ((4 - list_MyList_start) / 2), }, ... { .ptNext = &(s_tListMyListPool[ (5 - list_MyList_start)/2 + 1]), .chID = ((6 - list_MyList_start) / 2), }, { .ptNext = NULL, .chID = ((7 - list_MyList_start) / 2), },
}; static node_item_t *s_ptListMyListRoot = s_tListMyListPool;


【參數宏也支持重載?】


  什么是參數宏的重載?——要回答這個問題,哪怕你連“重載(overload)”是什么都不知道也不要緊,我們來看一個最實際的例子:在前面的文章中,我們不止一次使用過一個膠水宏 CONNECT3,它的作用是將三個字符串粘連在一起變成一個完整的字符串。如果我們要粘連的字符串數量不同,比如,2個、4個、5個……n個,我們就要編寫對應的版本:

#define __CONNECT2(__0, __1)            __0##__1#define __CONNECT3(__0, __1, __2)       __0##__1##__2#define __CONNECT4(__0, __1, __2, __3)  __0##__1##__2##__3...#define __CONNECT8(__0, __1, __2, __3, __4, __5, __6, __7)      \           __0##__1##__2##__3##__4##__5##__6##__7#define __CONNECT9(__0, __1, __2, __3, __4, __5, __6, __7, __8) \           __0##__1##__2##__3##__4##__5##__6##__7##__8           //! 安全“套”           #define CONNECT2(__0, __1)             __CONNECT2(__0, __1)#define CONNECT3(__0, __1, __2)        __CONNECT3(__0, __1, __2)#define CONNECT4(__0, __1, __2, __3)   __CONNECT4(__0, __1, __2, __3)...#define CONNECT8(__0, __1, __2, __3, __4, __5, __6, __7)        \    __CONNECT8(__0, __1, __2, __3, __4, __5, __6, __7)#define CONNECT9(__0, __1, __2, __3, __4, __5, __6, __7, __8)   \    __CONNECT9(__0, __1, __2, __3, __4, __5, __6, __7, __8)

這里定義了最大連接9個的CONNECT版本,看似麻煩,實際上復制粘貼、一勞永逸——還是挺劃算的——當然,如果你比較“耿直”,還可以做得更多,比如16個。所謂宏的重載是說:我們不必親自去數要粘貼的字符串的數量而“手工選取正確的版本”,而直接讓編譯器自己替我們挑選。


比如,我們舉一個組裝16進制數字的例子:

#define HEX_U8_VALUE(__B1, __B0)                         \      CONNECT3(0x, __B1, __B0)
#define HEX_U16_VALUE(__B3, __B2, __B1, __B0)     \   CONNECT5(0x, __B3, __B2, __B1, __B0)            #define HEX_U32_VALUE(__B7, __B6, __B4, __B4, __B3, __B2, __B1, __B0)\     CONNECT9(0x, __B7, __B6, __B4, __B4, __B3, __B2, __B1, __B0)

在支持重載的情況下,我們希望這樣使用:

#define HEX_U8_VALUE(__B1, __B0)                         \      CONNECT(0x, __B1, __B0)
#define HEX_U16_VALUE(__B3, __B2, __B1, __B0) \      CONNECT(0x, __B3, __B2, __B1, __B0) #define HEX_U32_VALUE(__B7, __B6, __B4, __B4, __B3, __B2, __B1, __B0)\      CONNECT(0x, __B7, __B6, __B4, __B4, __B3, __B2, __B1, __B0)

如你所見,無論實際給出的參數是多少個,我們都可以使用同一個參數宏CONNECT(),而CONNCT() 會自動計算用戶給出參數的個數,從而正確的替換為CONNETn()版本。假設這一切都是可能做到的,那么實際上我們還可以對上述宏定義進行簡化:

#define HEX_VALUE(...)          CONNECT(0x, __VA_ARGS__)
#define HEX_U8_VALUE(__B1, __B0) \ HEX_VALUE(__B1, __B0)
#define HEX_U16_VALUE(__B3, __B2, __B1, __B0) \ HEX_VALUE(__B3, __B2, __B1, __B0) #define HEX_U32_VALUE(__B7, __B6, __B4, __B4, __B3, __B2, __B1, __B0)\ HEX_VALUE(__B7, __B6, __B4, __B4, __B3, __B2, __B1, __B0)

是的,一個 HEX_VALUE() 就足夠了,你隨便添幾個參數都行(只要小于等于你實現的CONNECTn的數量)。


既然前景如此誘人,怎么實現宏的重載呢?為了簡化這個問題,我們假設有一個“魔法宏”:它可以告訴我們用戶實際傳遞了多少個參數,我們不妨叫它 VA_NUM_ARGS()
#define VA_NUM_ARGS(...)         /* 這里暫時先不管怎么實現 */
借助它,我們可以這樣來編寫宏 CONNECT():
#define CONNECT(...)                                \    CONNECT2(CONNECT, VA_NUM_ARGS(__VA_ARGS__))     /*part1*/\        (__VA_ARGS__)                               /*part2*/
當用戶使用CONNECT()時,VA_NUM_ARGS(__VA_ARGS__)會給出參數的數量;"part1" 中 CONNECT2() 的作用就是將 字符串“CONNCET”與這個數組組合起來變成一個新的“參數宏的名字”;而 "part2" 的作用則是給這個組裝出來的參數宏傳遞參數。如果你覺得頭暈了,我們不妨來舉一個例子:

假設用戶想用 HEX_VALUE() 組裝一個數字
uint16_t hwValue = HEX_VALUE(D, E, A, D);   //! 0xDEAD
它會被首先展開為:
uint16_t hwValue = CONNECT(0x, D, E, A, D); 
進而
uint16_t hwValue =     CONNECT2(CONNECT, VA_NUM_ARGS(0x, D, E, A, D))        (0x, D, E, A, D);
由于VA_NUM_ARGS() 告訴我們有5個參數,最終實際展開為:
uint16_t hwValue =     CONNECT5        (0x, D, E, A, D);
完美!那么我們就來逆推這個問題:如何實現我們的魔法宏“VA_NUM_ARGS()” 呢?答案如下:
#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,_6,_7,_8,_9,__N,...) __N#define VA_NUM_ARGS(...)                                                \            VA_NUM_ARGS_IMPL(__VA_ARGS__,9,8,7,6,5,4,3,2,1)
這里,首先構造了一個特殊的參數宏,VA_NUM_ARGS_IMPL()
  • 在涉及"..."之前,它要用用戶至少傳遞10個參數;

  • 這個宏的返回值就是第十個參數的內容;

  • 多出來的部分會被"..."吸收掉,不會產生任何后果


VA_NUM_ARGS() 的巧妙在于,它把__VA_ARGS__放在了參數列表的最前面,并隨后傳遞了 "9,8,7,6,5,4,3,2,1" 這樣的序號:

__VA_ARGS__里有1個參數時,“1”對應第十個參數__N,所以返回值是1__VA_ARGS__里有2個參數時,“2”對應第十個參數__N,所以返回值是2...__VA_ARGS__里有9個參數時,"9"對應第十個參數__N,所以返回值是9

如果覺得上述過程似懂非懂,我們不妨對前面的例子做一個展開:

VA_NUM_ARGS(0x, D, E, A, D)
展開為:
VA_NUM_ARGS_IMPL(0x, D, E, A, D,9,8,7,6,5,4,3,2,1)
從左往右數,第十個參數,正好是“5”。

宏的重載非常有用,可以極大的簡化用戶"選擇困難",你甚至可以將VA_NUM_ARGS() 與 函數名結合在一起,從而實現簡單的函數重載(即,函數參數不同的時候,可以通過這種方法在編譯階段有預編譯器根據用戶輸入參數的數量自動選擇對應的函數),比如:

extern device_write1(const char *pchString);extern device_write2(uint8_t *pchStream, uint_fast16_t hwLength);extern device_write3(uint_fast32_t wAddress, uint8_t *pchStream, uint_fast16_t hwLength);
#define device_write(...) \            CONNECT2(device_write, VA_NUM_ARGS(__VA_ARGS__))  \             (__VA_ARGS__)            

                

使用時:

device_write("hello world");       //!< 發送字符串
extern uint8_t chBuffer[32];device_write(chBuffer, 32);        //!< 發送緩沖
//! 向指定偏移量寫數據#define LCD_DISP_MEM_START 0x4000xxxxextern uint16_t hwDisplayBuffer[320*240];device_write( LCD_DISP_MEM_START,     (uint8_t *)hwDisplayBuffer,     sizeof(hwDisplayBuffer));

往期推薦


1、深度好文|面試官:進程和線程,我只問這19個問題

2、他來了,他來了,C++17新特性精華都在這了

3、一文讓你搞懂設計模式

4、C++11新特性,所有知識點都在這了!




如果喜歡這篇文章,請點贊、在看,支持一下哦~謝謝!

亚洲欧美第一页_禁久久精品乱码_粉嫩av一区二区三区免费野_久草精品视频
欧美大片18| 可以看av的网站久久看| 日韩视频精品在线观看| 亚洲无线一线二线三线区别av| 亚洲欧美综合| 免费在线观看一区二区| 国产精品久久久久77777| 在线看片欧美| 国产精品综合色区在线观看| 亚洲欧洲综合| 久久精品99国产精品| 欧美一区二区三区四区在线观看| 欧美日本高清| 国产欧美日韩视频| 欧美亚州韩日在线看免费版国语版| 一区在线观看视频| 欧美亚洲免费电影| 欧美色图首页| 亚洲日本无吗高清不卡| 久久亚洲国产精品日日av夜夜| 国产精品羞羞答答| 亚洲一本视频| 国产精品爱久久久久久久| 国内揄拍国内精品久久| 午夜一区二区三区不卡视频| 欧美新色视频| 欧美影院久久久| 欧美手机在线| 日韩午夜精品| 欧美剧在线观看| 99在线热播精品免费| 久久综合五月天婷婷伊人| 韩国一区二区三区在线观看| 久久精品国产99| 国产一区二区三区在线观看视频| 欧美在线精品免播放器视频| 国产精品天天摸av网| 亚洲欧美视频一区| 国产精品视频内| 亚洲专区欧美专区| 国产精品视频免费观看www| 亚洲一区美女视频在线观看免费| 欧美日韩中文字幕精品| 一本久久a久久免费精品不卡| 欧美女激情福利| 亚洲精品一级| 欧美色中文字幕| 亚洲午夜精品久久| 国产精品少妇自拍| 午夜精品999| 国产精品丝袜白浆摸在线| 亚洲男人影院| 国产偷国产偷亚洲高清97cao| 午夜精品一区二区三区在线 | 久久一综合视频| 一区在线视频| 久久成人资源| 国色天香一区二区| 久久婷婷国产麻豆91天堂| 亚洲国产一区在线| 欧美视频三区在线播放| 久久精品一区二区国产| 亚洲免费电影在线观看| 国产女人18毛片水18精品| 欧美aⅴ99久久黑人专区| 亚洲在线不卡| 最新日韩av| 国产深夜精品福利| 欧美精品亚洲一区二区在线播放| 欧美一区二区三区视频在线 | 鲁大师成人一区二区三区| 日韩视频欧美视频| 国产日韩综合| 欧美色视频在线| 久久综合福利| 欧美在线不卡视频| 在线视频你懂得一区二区三区| 国产一区免费视频| 国产精品第一区| 欧美国产视频一区二区| 久久精品视频在线看| 亚洲尤物在线视频观看| 亚洲精选一区| 亚洲国产精品黑人久久久| 国产日韩精品一区二区三区在线| 欧美日韩在线亚洲一区蜜芽| 欧美激情二区三区| 农村妇女精品| 免费视频亚洲| 久久在线观看视频| 久久精品视频在线| 午夜在线a亚洲v天堂网2018| 在线视频你懂得一区| 亚洲日韩欧美一区二区在线| 在线观看日韩| 黄色国产精品| 国产亚洲精品久久久久婷婷瑜伽 | 久久亚洲精选| 久久成人免费| 欧美一区免费视频| 午夜亚洲伦理| 西瓜成人精品人成网站| 亚洲视频在线观看视频| 一本一本大道香蕉久在线精品| 亚洲肉体裸体xxxx137| 亚洲国产精品日韩| 亚洲国产日韩在线| 久久精品国产久精国产爱| 亚洲午夜羞羞片| 一区二区三区欧美成人| 999在线观看精品免费不卡网站| 亚洲精品在线二区| 日韩小视频在线观看专区| 日韩亚洲在线观看| 一区二区三区四区在线| 亚洲小说欧美另类婷婷| 亚洲在线观看视频| 欧美亚洲视频在线观看| 欧美一区二区成人| 久久精品人人做人人综合| 久久久精品999| 麻豆av一区二区三区久久| 蜜臀a∨国产成人精品| 欧美高清不卡在线| 欧美日韩国产123| 国产精品高清一区二区三区| 国产欧美日韩精品a在线观看| 国产一区二区久久| 亚洲电影免费观看高清完整版在线观看 | 国产亚洲女人久久久久毛片| 国产亚洲成年网址在线观看| 黄色av日韩| 亚洲欧洲日本一区二区三区| 日韩一区二区精品葵司在线| 免费中文字幕日韩欧美| 欧美成人免费播放| 欧美午夜精品久久久| 国产欧美综合一区二区三区| 尤物在线精品| 99视频国产精品免费观看| 亚洲综合第一| 久久欧美中文字幕| 欧美成ee人免费视频| 欧美女主播在线| 国产精品久久久久免费a∨| 国产亚洲欧美色| 亚洲乱码国产乱码精品精天堂| 亚洲图片欧美午夜| 久久精品亚洲一区二区| 欧美大片免费观看| 国产精品剧情在线亚洲| 韩国欧美国产1区| 99精品视频免费在线观看| 欧美在线免费| 欧美人与禽猛交乱配视频| 国产欧美精品日韩精品| 亚洲国产日韩欧美在线图片| 亚洲一区欧美二区| 欧美成人免费视频| 国产欧美一区二区精品婷婷 | 午夜精品久久久久久久男人的天堂 | 欧美午夜视频在线观看| 国产主播一区二区三区四区| 99v久久综合狠狠综合久久| 欧美一区二区三区日韩| 欧美日韩性生活视频| 影音先锋亚洲视频| 午夜精品999| 欧美日韩国产影片| 激情一区二区| 午夜国产欧美理论在线播放| 欧美日韩国产一区精品一区| 精品电影一区| 欧美一区二区三区免费在线看| 欧美精品综合| 在线观看日韩www视频免费| 香蕉久久夜色| 欧美日韩中文字幕日韩欧美| 亚洲国产美女| 久久久福利视频| 国产精品综合不卡av| 日韩一区二区精品| 欧美96在线丨欧| 黄色精品在线看| 欧美影视一区| 国产精品一区一区三区| 在线视频免费在线观看一区二区| 欧美成ee人免费视频| 狠狠色综合色综合网络| 欧美亚洲色图校园春色| 国产精品国产福利国产秒拍| 9l国产精品久久久久麻豆| 欧美肥婆在线| 亚洲激情欧美| 免费久久99精品国产| 在线观看91精品国产入口| 久久久久久精| 伊人久久亚洲美女图片| 久久久91精品国产| 狠狠狠色丁香婷婷综合久久五月 |