精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

結合實例深入理解C++對象的內存布局

開發
本篇文章試著從實際的例子出發,幫助大家對 C++ 類成員變量和函數在內存布局有個直觀的理解

作者 | daemonzhao

通過實例來深入理解 C++ 對象的內存布局,包括基礎數據類、帶方法的類、私有成員、靜態成員、類繼承等。通過 GDB 查看對象的內存布局,探討成員變量、成員方法、虛函數表等在內存中的存儲位置和實現細節,幫助大家對 C++ 類成員變量和函數在內存布局有個直觀的理解。

因為二進制使用了不同版本的 proto 對象,對象的內存布局不一致導致讀、寫成員的內存地址錯亂,進而導致進程 crash 掉。這之中會出現下面的問題:

  • 對象在內存中是怎么布局的?
  • 成員方法是如何拿到成員變量的地址?

這些其實涉及 C++ 的對象模型,《深度探索 C++對象模型:Inside the C++ Object Model》這本書全面聊了這個問題,非常值得一讀。不過這本書讀起來并不容易,有的內容讀過后如果沒有加以實踐,也很難完全理解。本篇文章試著從實際的例子出發,幫助大家對 C++ 類成員變量和函數在內存布局有個直觀的理解,后面再讀這本書也會容易理解些。

簡單對象內存分布

首先以一個最簡單的 Basic 類為例,來看看只含有基本數據類型的對象是怎么分配內存的。

#include <iostream>
using namespace std;

class Basic {
public:
    int a;
    double b;
};

int main() {
    Basic temp;
    temp.a = 10;
    return 0;
}

編譯運行后,可以用 GDB 來查看對象的內存分布。如下圖:

對象 temp 的起始地址是 0x7fffffffe3b0,這是整個對象在內存中的位置。成員變量 a 的地址也是 0x7fffffffe3b0,表明 int a 是對象 temp 中的第一個成員,位于對象的起始位置。成員變量 b 的類型為 double,其地址是 0x7fffffffe3b8(a 的地址+8),內存布局如下圖:

這里 int 類型在當前平臺上占用 4 個字節(可以用 sizeof(int)驗證),而這里 double 成員的起始地址與 int 成員的起始地址之間相差 8 個字節,說明在 a 之后存在內存對齊填充(具體取決于編譯器的實現細節和平臺的對齊要求)。內存對齊要求數據的起始地址在某個特定大小(比如 4、8)的倍數上,這樣可以優化硬件和操作系統訪問內存的效率。這是因為許多處理器訪問對齊的內存地址比訪問非對齊地址更快。

另外在不進行內存對齊的情況下,較大的數據結構可能會跨越多個緩存行或內存頁邊界,這會導致額外的緩存行或頁的加載,降低內存訪問效率。不過大多時候我們不需要手動管理內存對齊,編譯器和操作系統會自動處理這些問題。

帶方法的對象內存分布

帶有方法的類又是什么樣呢?接著上面的例子,在類中增加一個方法 setB,用來設置其中成員 b 的值。

#include <iostream>

class Basic {
public:
    int a;
    double b;

    void setB(double value) {
        b = value; // 直接訪問成員變量b
    }
};

int main() {
    Basic temp;
    temp.a = 10;
    temp.setB(3.14);
    return 0;
}

用 GDB 打印 temp 對象以及成員變量的地址,發現內存布局和前面不帶方法的完全一樣。整個對象 size 依然是 16,a 和 b 的內存地址分布也是一致的。那么新增加的成員方法存儲在什么位置?成員方法中又是如何拿到成員變量的地址呢?

1.成員方法內存布局

可以在 GDB 里面打印下成員方法的地址,如下圖所示。

回憶下 Linux 中進程的內存布局,其中文本段(也叫代碼段)是存儲程序執行代碼的內存區域,通常是只讀的,以防止程序在運行時意外或惡意修改其執行代碼。這里 setB 方法地址 0x5555555551d2 就是位于程序的文本段內,可以在 GDB 中用 info target 驗證一下:

其中 .text 段的地址范圍是 0x0000555555555060 - 0x0000555555555251,setB 剛好在這個范圍內。至此前面第一個問題有了答案,成員方法存儲在進程的文本段,添加成員方法不會改變類實例對象的內存布局大小,它們也不占用對象實例的內存空間。

2.成員變量尋址

那么成員方法中又是如何拿到成員變量的地址呢?在解決這個疑問前,先來仔細看下 setB 的函數原型(void (*)(Basic * const, double)),這里函數的第一個參數是Basic* 指針,而在代碼中的調用是這樣:temp.setB(3.14)。這種用法其實是一種語法糖,編譯器在調用成員函數時自動將當前對象的地址作為 this 指針傳遞給了函數的。

(gdb) p &Basic::setB(double)
$7 = (void (*)(Basic * const, double)) 0x5555555551d2 <Basic::setB(double)>

這里參數傳遞了對象的地址,但是在函數里面是怎么拿到成員變量 b 的地址呢?我們在調用 setB 的地方打斷點,執行到斷點后,用 step 進入到函數,然后查看相應寄存器的值和匯編代碼。整個過程如下圖:

這里的匯編代碼展示了如何通過 this 指針和偏移量訪問 b。可以分為兩部分,第一部分是處理 this 指針和參數,第二部分是找到成員 b 的內存位置然后進行賦值。

  • 參數傳遞部分。這里mov %rdi,-0x8(%rbp)將 this 指針(通過 rdi 寄存器傳入)保存到棧上。將 double 類型的參數 value 通過 xmm0 寄存器傳入保存到棧上。這是 x86_64 機器下 GCC 編譯器的傳參規定,我們可以通過打印 $rdi 保存的地址來驗證確實是 temp 對象的開始地址。
  • 對象賦值部分。mov -0x8(%rbp),%rax 將 this 指針從棧上加載到 rax 寄存器中。類似的,movsd -0x10(%rbp),%xmm0 將參數 value 從棧上重新加載到 xmm0 寄存器中。movsd %xmm0,0x8(%rax) 將 value 寫入到 this 對象的 b 成員。這里 0x8(%rax) 表示 rax(即 this 指針)加上 8 字節的偏移,這個偏移正是成員變量 b 在 Basic 對象中的位置。

這個偏移是什么時候,怎么算出來的呢?其實成員變量的地址相對于對象地址是固定的,對象的地址加上成員變量在對象內的偏移量就是成員變量的實際地址。編譯器在編譯時,基于類定義中成員變量的聲明順序和編譯器的內存布局規則,計算每個成員變量相對于對象起始地址的偏移量。然后在運行時,通過基地址(即對象的地址)加上偏移量,就能夠計算出每個成員變量的準確地址。這個過程對于程序員來說是透明的,由編譯器和運行時系統自動處理。

3.函數調用約定與優化

上面的匯編代碼中,setB 的兩個參數,都是從寄存器先放到棧上,接著又從棧上放到寄存器進行操作,為什么要移來移去多此一舉呢?要回答這個問題,需要先了解函數的調用約定和寄存器使用。在 x86_64 架構的系統調用約定中,前幾個整數或指針參數通常通過寄存器(如 rdi, rsi, rdx, 等)傳遞,而浮點參數通過 xmm0 到 xmm7 寄存器傳遞。這種約定目的是為了提高函數調用的效率,因為使用寄存器傳遞參數比使用棧更快。

而將寄存器上的參數又移動到棧上,是為了保證寄存器中的值不被覆蓋。因為寄存器是有限的資源,在函數中可能會被多次用于不同的目的。將值保存到棧上可以讓函數內部自由地使用寄存器,而不必擔心覆蓋調用者的數據。

接著又將-0x8(%rbp) 放到 rax 寄存器,然后再通過movsd %xmm0,0x8(%rax)寫入成員變量 b 的值,為啥不直接從xmm0寄存器寫到基于 rbp 的偏移地址呢?這是因為 x86_64 的指令集和其操作模式通常支持使用寄存器間接尋址方式訪問數據。使用rax等通用寄存器作為中間步驟,是一種更通用和兼容的方法。

當然上面編譯過程沒有開啟編譯優化,所以編譯器采用了直接但效率不高的代碼生成策略,包括將參數和局部變量頻繁地在棧與寄存器間移動。而編譯器的優化策略可能會影響參數的處理方式。如果我們開啟編譯優化,如下:

$ g++ basic_method.cpp -o basic_method_O2 -O2 -g -std=c++11

生成的 main 函數匯編部分如下:

(gdb) disassemble /m main
=> 0x0000555555555060 <+0>: xor    %eax,%eax
   0x0000555555555062 <+2>: ret
   0x0000555555555063: data16 nopw %cs:0x0(%rax,%rax,1)
   0x000055555555506e: xchg   %ax,%ax

在 O2 優化級別下,編譯器認定 main 函數中的所有操作(包括創建 Basic 對象和對其成員變量的賦值操作)對程序的最終結果沒有影響,因此它們都被優化掉了。這是編譯器的“死代碼消除”,直接移除那些不影響程序輸出的代碼部分。

特殊成員內存分布

上面的成員都是 public 的,如果是 private(私有) 變量,私有方法呢?另外,靜態成員變量或者靜態成員方法,在內存中又是怎么布局呢?

1.私有成員

先來看私有成員,接著上面的例子,增加私有成員變量和方法。整體代碼如下:

#include <iostream>

class Basic {
public:
    int a;
    double b;

    void setB(double value) {
        b = value; // 直接訪問成員變量b
        secret(b);
    }
private:
    int c;
    double d;

    void secret(int temp) {
        d = temp + c;
    }
};

int main() {
    Basic temp;
    temp.a = 10;
    temp.setB(3.14);
    return 0;
}

編譯之后,通過 GDB,可以打印出所有成員變量的地址,發現這里私有變量的內存布局并沒有什么特殊地方,也是依次順序存儲在對象中。私有的方法也沒有特殊地方,一樣存儲在文本段。整體布局如下如:

那么 private 怎么進行可見性控制的呢?首先編譯期肯定是有保護的,這個很容易驗證,我們無法直接訪問 temp.c ,或者調用 secret 方法,因為直接會編譯出錯。

那么運行期是否有保護呢?我們來驗證下。前面已經驗證 private 成員變量也是根據偏移來找到內存位置的,我們可以在代碼中直接根據偏移找到內存位置并更改里面的值。

int* pC = reinterpret_cast<int*>(reinterpret_cast<char*>(&temp) + 16);
*pC = 12; // 直接修改c的值

這里修改后,可以增加一個 show 方法打印所有成員的值,發現這里 temp.c 確實被改為了 12。可見成員變量在運行期并沒有做限制,知道地址就可以繞過編譯器的限制進行讀寫了。那么私有的方法呢?

私有方法和普通成員方法一樣存儲在文本段,我們拿到其地址后,可以通過這個地址調用嗎?這里需要一些騷操作,我們在類定義中添加額外的接口來暴露私有成員方法的地址,然后通過成員函數指針來調用私有成員函數。整體代碼如下:

class Basic {
...
public:
    // 暴露私有成員方法的地址
    static void (Basic::*getSecretPtr())(int) {
        return &Basic::secret;
    }

...
}

int main() {
    // ...
   void (Basic::*funcPtr)(int) = Basic::getSecretPtr();
    // 調用私有成員函數
    (temp.*funcPtr)(10);
    // ...
}

上面代碼正常運行,你可以通過 print 打印調用前后成員變量的值來驗證。看來對于成員函數來說,只是編譯期不讓直接調用,運行期并沒有保護,我們可以繞過編譯限制在對象外部調用。

當然實際開發中,千萬不要直接通過地址偏移來訪問私有成員變量,也不要通過各種騷操作來訪問私有成員方法,這樣不僅破壞了類的封裝性,而且是不安全的。

2.靜態成員

每個熟悉 c++ 類靜態成員的人都知道,靜態成員變量在類的所有實例之間共享,不管你創建了多少個類的對象,靜態成員變量只有一份數據。靜態成員變量的生命周期從它們被定義的時刻開始,直到程序結束。靜態成員方法不依賴于類的任何實例來執行,主要用在工廠方法、單例模式的實例獲取方法、或其他與類的特定實例無關的工具函數。

下面以一個具體的例子,來看看靜態成員變量和靜態成員方法的內存布局以及實現特點。繼續接著前面代碼例子,這里省略掉其他無關代碼了。

#include <iostream>

class Basic {
// ...
public:
    static float alias;
    static void show() {
        std::cout << alias << std::endl;
    }
};

float Basic::alias = 0.233;
int main() {
    // ...
    temp.show();
    return 0;
}

簡單的打印 temp 和 alias 地址,發現兩者之間差異挺大。temp 地址是 0x7fffffffe380,Basic::alias 是 0x555555558048,用 info target 可以看到 alias 在程序的 .data 內存空間范圍 0x0000555555558038 - 0x000055555555804c 內。進一步驗證了下,.data段用于存儲已初始化的全局變量和靜態變量,注意這里需要是非零初始值。

對于沒有初始化,或者初始化為零的全局變量或者靜態變量,是存儲在 .bss 段內的。這個也很好驗證,把上面 alias 的值設為 0,重新查看內存位置,就能看到確實在 .bss 段內了。對于全局變量或者靜態變量,為啥需要分為這兩個段來存儲,而不是合并為一個段來存儲呢?

這里主要是考慮到二進制文件磁盤空間大小以及加載效率。在磁盤上,.data 占用實際的磁盤空間,因為它需要存儲具體的初始值數據。.bss段不占用實際的存儲空間,只需要在程序加載時由操作系統分配并清零相應的內存即可,這樣可以減少可執行文件的大小。在程序啟動時,操作系統可以快速地為.bss段分配內存并將其初始化為零,而無需從磁盤讀取大量的零值數據,可以提高程序的加載速度。這里詳細的解釋也可以參考 Why is the .bss segment required?。

靜態方法又是怎么實現呢?我們先輸出內存地址,發現在 .text 代碼段,這點和其他成員方法是一樣的。不過和成員方法不同的是,第一個參數并不是 this 指針了。在實現上它與普通的全局函數類似,主要區別在于它們的作用域是限定在其所屬的類中。

類繼承的內存布局

當然,既然是在聊面向對象的類,那就少不了繼承了。我們還是從具體例子來看看,在繼承情況下,類的內存布局情況。

1.不帶虛函數的繼承

先來看看不帶虛函數的繼承,示例代碼如下:

#include <iostream>

class Basic {
public:
    int a;
    double b;

    void setB(double value) {
        b = value; // 直接訪問成員變量b
    }
};

class Derived : public Basic {
public:
    int c;
    void setC(int value) {
        c = value; // 直接訪問成員變量c
    }
};

int main() {
    Derived temp;
    temp.a = 10;
    temp.setB(3.14);
    temp.c = 1;
    temp.setC(2);
    return 0;
}

編譯運行后,用 GDB 打印成員變量的內存分布,發現 Derived 類的對象在內存中的布局首先包含其基類Basic的所有成員變量,緊接著是 Derived 類自己的成員變量。整體布局如下圖:

其實 C++ 標準并沒有規定在繼承中,基類和派生類的成員變量之間的排列順序,編譯器可以自由發揮的。但是大部分編譯器在實現中,都是基類的成員變量在派生類的成員變量之前,為什么這么做呢?因為這樣實現,使對象模型變得更簡單和直觀。不論是基類還是派生類,對象的內存布局都是連續的,簡化了對象創建、復制和銷毀等操作的實現。我們通過派生類對象訪問基類成員與直接使用基類對象訪問時完全一致,一個派生類對象的前半部分就是一個完整的基類對象。

對于成員函數(包括普通函數和靜態函數),它們不占用對象實例的內存空間。不論是基類的成員函數還是派生類的成員函數,它們都存儲在程序的代碼段中(.text 段)。

2.帶有虛函數的繼承

帶有虛函數的繼承,稍微有點復雜了。在前面繼承例子基礎上,增加一個虛函數,然后在 main 中用多態的方式調用。

#include <iostream>

class Basic {
public:
    int a;
    double b;

    virtual void printInfo() {
        std::cout << "Basic: a = " << a << ", b = " << b << std::endl;
    }

    virtual void printB() {
        std::cout << "Basic in B" << std::endl;
    }

    void setB(double value) {
        b = value; // 直接訪問成員變量b
    }
};

class Derived : public Basic {
public:
    int c;

    void printInfo() override {
        std::cout << "Derived: a = " << a << ", b = " << b << ", c = " << c << std::endl;
    }

    void setC(int value) {
        c = value; // 直接訪問成員變量c
    }
};

int main() {
    Derived derivedObj;
    derivedObj.a = 10;
    derivedObj.setB(3.14);
    derivedObj.c = 1;
    derivedObj.setC(2);

    Basic* ptr = &derivedObj; // 基類指針指向派生類對象
    ptr->printInfo(); // 多態調用
    ptr->printB(); // 調用

    Basic  basicObj;
    basicObj.a = 10;
    basicObj.setB(3.14);

    Basic* anotherPtr = &basicObj;
    anotherPtr->printInfo();
    anotherPtr->printB();
    return 0;
}

上面代碼中,Basic* ptr = &derivedObj; 這一行用一個基類指針指向派生類對象,當通過基類指針調用虛函數 ptr->printInfo();時,將在運行時解析為 Derived::printInfo() 方法,這是就是運行時多態。對于 ptr->printB(); 調用,由于派生類中沒有定義 printB() 方法,所以會調用基類的 printB() 方法。

那么在有虛函數繼承的情況下,對象的內存布局是什么樣?虛函數的多態調用又是怎么實現的呢?實踐出真知,我們可以通過 GDB 來查看對象的內存布局,在此基礎上可以驗證虛函數表指針,虛函數表以及多態調用的實現細節。這里先看下 Derived 類對象的內存布局,如下圖:

可以看到派生類對象的開始部分(地址 0x7fffffffe370 處)有一個 8 字節的虛函數表指針 vptr(指針地址 0x555555557d80),這個指針指向一個虛函數表(vtable),虛函數表中存儲了虛函數的地址,一共有兩個地址 0x55555555538c 和 0x555555555336,分別對應Derived 類中的兩個虛函數 printInfo 和 printB。基類的情況類似,下面畫一個圖來描述更清晰些:

現在搞清楚了虛函數在類對象中的內存布局。在編譯器實現中,虛函數表指針是每個對象實例的一部分,占用對象實例的內存空間。對于一個實例對象,通過其地址就能找到對應的虛函數表,然后通過虛函數表找到具體的虛函數地址,實現多態調用。那么為什么必須通過引用或者指針才能實現多態調用呢?看下面 3 個調用,最后一個沒法多態調用。

Basic& ref = derivedObj;
Basic* ptr = &derivedObj;
Basic dup = derivedObj; // 沒法實現多態調用

我們用 GDB 來看下這三種對象的內存布局,如下圖:

指針和引用在編譯器底層沒有區別,ref 和 ptr 的地址一樣,就是原來派生類 derivedObj 的地址0x7fffffffe360,里面的虛函數表指針指向派生類的虛函數表,所以可以調用到派生類的 printInfo。而這里的 dup 是通過拷貝構造函數生成的,編譯器執行了隱式類型轉換,從派生類截斷了基類部分,生成了一個基類對象。dup 中的虛函數表指針指向的是基類的虛函數表,所以調用的是基類的 printInfo。

從上面 dup 虛函數表指針的輸出也可以看到,虛函數表不用每個實例一份,所有對象實例共享同一個虛函數表即可。虛函數表是每個多態類一份,由編譯器在編譯時創建。

當然,這里是 Mac 平臺下 Clang 編譯器對于多態的實現。C++ 標準本身沒有規定多態的實現細節,沒有說一定要有虛函數表(vtable)和虛函數表指針(vptr)來實現。這是因為 C++標準關注的是行為和語義,確保我們使用多態特性時能夠得到正確的行為,但它不規定底層的內存布局或具體的實現機制,這些細節通常由編譯器的實現來決定。

不同編譯器的實現也可能不一樣,許多編譯器為了訪問效率,將虛函數表指針放在對象內存布局的開始位置。這樣,虛函數的調用可以快速定位到虛函數表,然后找到對應的函數指針。如果類有多重繼承,情況可能更復雜,某些編譯器可能會采取不同的策略來安排虛函數表指針的位置,或者一個對象可能有多個虛函數表指針。

地址空間布局隨機化

前面的例子中,如果用 GDB 多次運行程序,對象的虛擬內存地址每次都一樣,這是為什么呢?

我們知道現代操作系統中,每個運行的程序都使用虛擬內存地址空間,通過操作系統的內存管理單元(MMU)映射到物理內存的。虛擬內存有很多優勢,包括提高安全性、允許更靈活的內存管理等。為了防止緩沖區溢出攻擊等安全漏洞,操作系統還會在每次程序啟動時隨機化進程的地址空間布局,這就是地址空間布局隨機化(ASLR,Address Space Layout Randomization)。

在 Linux 操作系統上,可以通過 cat /proc/sys/kernel/randomize_va_space 查看當前系統的 ASLR 是否啟用,基本上默認都是開啟狀態(值為 2),如果是 0,則是禁用狀態。

前面使用 GDB 進行調試時,之所以觀察到內存地址是固定不變的,這是因為 GDB 默認禁用了 ASLR,以便于調試過程中更容易重現問題。可以在使用 GDB 時啟用 ASLR,從而讓調試環境更貼近實際運行環境。啟動 GDB 后,可以通過下面命令開啟地址空間的隨機化。

(gdb) set disable-randomization off

之后再多次運行,這里的地址就會變化了。

總結

C++ 的對象模型是一個復雜的話題,涉及到類的內存布局、成員變量和成員函數的訪問、繼承、多態等多個方面。本文從實際例子出發,幫助大家對 C++ 對象的內存布局有了一個直觀的認識。

簡單總結下本文的核心結論:

  • 對象的內存布局是連續的,成員變量按照聲明的順序存儲在對象中,編譯器會根據類定義計算每個成員變量相對于對象起始地址的偏移量。
  • 成員方法存儲在進程的文本段,不占用對象實例的內存空間,通過 this 指針和偏移量訪問成員變量。
  • 私有成員變量和方法在運行期并沒有保護,可以通過地址偏移繞過編譯器的限制進行讀寫,但是不推薦這樣做。
  • 靜態成員變量和靜態成員方法存儲在程序的數據段和代碼段,不占用對象實例的內存空間。
  • 繼承類的內存布局,編譯器一般會把基類的成員變量放在派生類的成員變量之前,使對象模型變得更簡單和直觀。
  • 帶有虛函數的繼承,對象的內存布局中包含虛函數表指針,多態調用通過虛函數表實現。虛函數實現比較復雜,這里只考慮簡單的單繼承。
  • 地址空間布局隨機化(ASLR)是現代操作系統的安全特性,可以有效防止緩沖區溢出攻擊等安全漏洞。GDB 默認禁用 ASLR,可以通過 set disable-randomization off 命令開啟地址空間的隨機化。
責任編輯:趙寧寧 來源: 騰訊技術工程
相關推薦

2022-07-06 08:05:52

Java對象JVM

2023-12-31 12:56:02

C++內存編程

2024-04-10 12:14:36

C++指針算術運算

2022-05-06 16:18:00

Block和 C++OC 類lambda

2024-04-30 08:38:31

C++

2023-09-12 11:44:02

C++數據對齊

2024-04-10 07:40:45

Java虛擬機內存

2023-11-05 12:05:35

JVM內存

2017-03-27 09:36:20

Flex布局計算

2019-10-22 08:11:43

Socket網絡通信網絡協議

2015-12-28 11:25:51

C++異常處理機制

2023-10-04 00:04:00

C++extern

2022-02-16 12:52:22

C++項目編譯器

2020-06-01 21:07:33

C11C++11內存

2021-11-26 00:00:48

JVM內存區域

2024-04-11 14:04:23

C++編程函數

2023-09-19 22:47:39

Java內存

2013-06-20 10:25:56

2020-11-04 15:35:13

Golang內存程序員

2024-01-03 13:38:00

C++面向對象編程OOP
點贊
收藏

51CTO技術棧公眾號

成人激情免费网站| 色爱综合av| 亚洲一区免费视频| 精品综合在线| 亚洲精品国产欧美在线观看| 欧美肥老太太性生活| 欧美成人午夜电影| 日韩福利视频在线| 日本电影在线观看| 久久久久久电影| 91手机在线播放| 一级黄色在线视频| 欧美理论在线| 亚洲香蕉伊综合在人在线视看| 亚洲一级免费在线观看| av成人福利| 国产精品成人免费精品自在线观看| 成人91免费视频| 中文字幕 欧美激情| 在线日韩av| 精品国产一区二区三区在线观看| 中文字幕第3页| 免费亚洲电影| 一二三区精品视频| 亚洲自拍三区| 黄色在线免费观看大全| 不卡的av网站| 2022国产精品| 91片黄在线观看喷潮| 久久国产精品久久久久久电车 | 一本一道久久综合狠狠老精东影业| 一区二区亚洲精品国产| 香港三级日本三级| 欧美视频三区| 91.麻豆视频| 激情伊人五月天| 牛牛精品在线| 亚洲女女做受ⅹxx高潮| 亚洲国产欧美日韩| 男人的天堂在线| 91蜜桃婷婷狠狠久久综合9色| av成人免费观看| 国产女同91疯狂高潮互磨| 日本中文字幕一区| 国产精品极品尤物在线观看| 中文字幕一区二区三区精品 | 少妇高潮一区二区三区| 日韩一卡二卡三卡国产欧美| 中文字幕亚洲影院| 欧美成人黄色| 欧美久久一二区| 日本人69视频| 先锋影音网一区二区| 欧美日韩一级二级| 天天干天天玩天天操| 99re久久| 欧美日韩精品欧美日韩精品| 中文字幕网av| 日韩欧美三区| 日韩一区二区三区免费看| 天天看片天天操| av在线精品| 日韩一区二区在线观看视频播放| 亚洲女人在线观看| 日日夜夜精品视频| 精品国产成人系列| 日本免费福利视频| 国产一区网站| 日韩亚洲精品视频| 杨钰莹一级淫片aaaaaa播放| 欧美人与禽猛交乱配视频| 欧美精品成人在线| √资源天堂中文在线| 日韩精品五月天| 国产精品免费在线免费| 国产情侣激情自拍| 成人av免费在线| 欧美亚洲丝袜| 免费在线视频欧美| 一区二区三区在线不卡| 超碰成人免费在线| 欧美大电影免费观看| 欧美日韩一区二区三区不卡| 91热视频在线观看| 免费成人三级| 在线观看久久av| 少妇aaaaa| 亚洲欧美春色| 国产欧美一区二区三区在线看| 国产精品久久久久久久免费| 国产成人精品免费网站| 美乳视频一区二区| 黄网站在线免费看| 激情成人在线视频| 最新天堂在线视频| 精品视频高潮| 日韩一区视频在线| 久久久.www| 蜜桃伊人久久| 3d精品h动漫啪啪一区二区| 亚洲AV成人无码一二三区在线| 中文字幕精品综合| 国产乱子伦农村叉叉叉| av一级久久| 亚洲视频第一页| 久热精品在线观看| 蜜桃视频免费观看一区| 国产三区精品| 国产精品va在线观看视色| 欧美午夜精品伦理| 亚洲午夜精品在线观看| 欧美一区二区三区激情视频| 国内精品美女av在线播放| 中文字幕理论片| 99久久久久久| 国产激情片在线观看| 日韩av一级| 日韩精品欧美激情| 久久久www成人免费毛片| 蜜桃视频在线观看一区| 欧美三级网色| 国产中文在线播放| 欧美不卡一区二区| 久久精品在线观看视频| 久久久xxx| 精品日本一区二区三区| 青草视频在线免费直播| 欧美日韩aaa| 综合 欧美 亚洲日本| 亚洲免费网站| 久久久久国产精品视频| 55av亚洲| 精品88久久久久88久久久| 免费国产羞羞网站美图| 麻豆成人免费电影| 日韩av在线电影观看| 成人av免费电影网站| 亚洲第一色在线| 久久久久久久久久99| 国产在线乱码一区二区三区| 亚洲精品人成| 国产91在线播放精品| 国产亚洲a∨片在线观看| 国产午夜精品久久久久| 91一区二区在线观看| 国产乱子伦农村叉叉叉| 色狼人综合干| 国产91在线播放精品91| 精品资源在线看| 91久久精品一区二区三| 欧美偷拍一区二区三区| 丝袜国产日韩另类美女| 日本高清一区| 69堂免费精品视频在线播放| 国产一区二区三区欧美| 中文字幕在线2019| 国产精品成人免费精品自在线观看| 五月天婷婷亚洲| 91av精品| 国产亚洲情侣一区二区无| 成人黄色动漫| 亚洲人成网在线播放| 成人黄色免费网| 亚洲欧洲三级电影| 日本黄色一级网站| 激情久久中文字幕| 久久久精彩视频| 国产精品99精品一区二区三区∴| 日韩一区二区在线视频| 性一交一乱一精一晶| 懂色av中文一区二区三区天美 | 天天干天天摸天天操| 黑丝美女久久久| 亚洲黄色小说视频| 日韩精品视频免费专区在线播放| 图片区乱熟图片区亚洲| 在线成人直播| 精品国产福利| 成人黄色免费短视频| www.亚洲男人天堂| 成人av一区二区三区在线观看| 亚洲一区二区精品久久av| 亚洲精品理论片| 黄页网站大全一区二区| 成人精品视频在线播放| 国产中文字幕一区二区三区| 成人国产精品日本在线| 久久影院午夜精品| 日韩中文在线视频| 免费的黄色av| 欧美在线你懂得| 欧美毛片在线观看| 26uuu色噜噜精品一区二区| 一区二区三区入口| 欧美三级免费| 午夜免费电影一区在线观看| а√中文在线天堂精品| 国产精品丝袜一区二区三区| 乱插在线www| 国产香蕉一区二区三区在线视频 | 中文字幕av免费在线观看| 99久久er热在这里只有精品15 | 国产精品国产三级在线观看| 97国产成人精品视频| 快射视频在线观看| 亚洲美女精品久久| 精品国产区一区二| 欧美综合在线视频| www.伊人久久| 亚洲一级二级在线| 日本黄色录像视频| 国产免费成人在线视频| 你懂的在线观看网站| 精品亚洲国产成人av制服丝袜| 亚洲自偷自拍熟女另类| 好吊日精品视频| 正在播放久久| 国产亚洲一区二区三区啪| 精品乱色一区二区中文字幕| 日韩在线亚洲| 成人激情视频在线| 欧美影视资讯| 国产成人精品电影| jizz一区二区三区| 色综合91久久精品中文字幕| 国产盗摄在线观看| 色哟哟入口国产精品| 国产专区在线播放| 亚洲剧情一区二区| 天天干视频在线观看| 亚洲第一色在线| 亚洲精品成人区在线观看| 777午夜精品免费视频| 亚洲网站在线免费观看| 欧美中文字幕亚洲一区二区va在线 | 国内精品视频一区| 欧美wwww| 欧美丰满少妇xxxxx做受| 成人福利在线观看视频| 精品国模在线视频| 国产传媒在线播放| 欧美精品在线免费| 视频在线这里都是精品| 久久成年人免费电影| 伊人手机在线| 欧美国产乱视频| 免费男女羞羞的视频网站在线观看| 久久天天躁夜夜躁狠狠躁2022| 黄色网址免费在线观看| 久久艳片www.17c.com| 国产成人高清精品| 欧美巨猛xxxx猛交黑人97人| 欧美家庭影院| 性色av一区二区三区免费 | 欧美1区2区3区| 超碰10000| 91久久在线| www.com毛片| 免费观看在线色综合| 日韩av片专区| 国产aⅴ精品一区二区三区色成熟| 色婷婷狠狠18禁久久| 成年人午夜久久久| 国产精品毛片一区二区| 欧美国产精品一区二区三区| 来吧亚洲综合网| 亚洲成人av电影| 男人天堂2024| 欧美日韩成人一区二区| 超碰在线播放97| 亚洲欧美日韩久久久久久| 最近高清中文在线字幕在线观看| 北条麻妃久久精品| xxxx另类黑人| 国产成人一区二区三区| 亚洲国产aⅴ精品一区二区三区| 亚洲在线www| 日韩欧美黄色| 在线观看欧美一区| 在线成人h网| 久久国产精品国产精品| 国产成人精品午夜视频免费| theav精尽人亡av| 亚洲视频一区在线| 久久精品国产成人av| 欧美综合一区二区三区| www香蕉视频| 中文在线不卡视频| 在线中文字幕第一页| 日本精品免费观看| 精品视频一二| 奇米视频888战线精品播放| 88国产精品视频一区二区三区| 国产av国片精品| 激情五月婷婷综合| 污污污www精品国产网站| 国产精品美女久久久久aⅴ| 国产一级片久久| 欧美三级欧美一级| 日本精品999| 久久夜色精品国产| 亚洲精品粉嫩美女一区| 国产精品v欧美精品v日韩| 日本道不卡免费一区| 成人黄色av片| 韩国女主播成人在线观看| 素人fc2av清纯18岁| 亚洲精品免费电影| 在线播放一级片| 国产午夜精品理论片a级探花| 最新av在线播放| 国产精品一区二区电影| 亚洲电影男人天堂| 日本一级黄视频| 精品一区二区在线免费观看| 男人操女人动态图| 亚洲成av人在线观看| 一区二区三区播放| 一区二区三区亚洲| 国产精品一区二区av影院萌芽| 99热99热| 欧美日本二区| 国偷自产av一区二区三区麻豆| 国产精品久久看| 真实新婚偷拍xxxxx| 精品亚洲一区二区三区在线播放| 欧美人与性动交α欧美精品图片| 国产日韩精品在线观看| 精品高清在线| 日韩精品一区二区三区不卡| 97aⅴ精品视频一二三区| www.99re7.com| 精品国免费一区二区三区| 亚洲淫性视频| 91久色国产| 欧美日韩精品免费观看视频完整| 中文字幕中文在线| 国产精品久久夜| 国产又黄又大又爽| 久热精品在线视频| 国产精品一区二区精品| 成人手机视频在线| 国产又黄又大久久| 黄色片子在线观看| 3d成人动漫网站| 在线播放免费av| 国产精品精品软件视频| 亚洲高清成人| 91视频啊啊啊| 色偷偷成人一区二区三区91 | 国产偷国产偷精品高清尤物| 无码人妻丰满熟妇精品区| 国产亚洲美女精品久久久| av亚洲一区| 黄黄视频在线观看| 国产成人精品亚洲777人妖 | 日韩一级二级三级| 91最新在线视频| 国产精品美女xx| 99亚洲一区二区| 国产aⅴ激情无码久久久无码| 欧美中文字幕一区| 麻豆传媒视频在线| 97人人澡人人爽| 中日韩男男gay无套| 亚洲第一综合网| 制服丝袜亚洲网站| 青青草原国产在线| 欧美日韩视频在线一区二区观看视频| 日本不卡视频一二三区| 波兰性xxxxx极品hd| 精品国产一二三| 欧美成人精品三级网站| 日日噜噜噜夜夜爽爽| jlzzjlzz亚洲日本少妇| 免费精品一区二区| 欧美成人合集magnet| 欧美色图婷婷| 亚洲天堂2018av| 亚洲综合激情网| 国产爆初菊在线观看免费视频网站| 91精品国产综合久久男男| 亚洲大胆在线| 在线观看天堂av| 欧美精品一区二| 欧美美女福利视频| 成人午夜视频在线观看免费| 国产片一区二区| 亚洲奶汁xxxx哺乳期| 国产成人精品综合| 欧美日韩专区| 亚洲最大成人综合网| 精品国产电影一区二区| jizz亚洲女人高潮大叫| av日韩一区二区三区| 国产精品伦理在线| 污污的视频网站在线观看| 国产日韩欧美在线观看| 亚洲一区中文| 日韩在线中文字幕视频|