青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

2006年7月17日

10:04:20

論調(diào)用約定

在C語(yǔ)言中,假設(shè)我們有這樣的一個(gè)函數(shù):

int function(int a,int b)

調(diào)用時(shí)只要用result = function(1,2)這樣的方式就可以使用這個(gè)函數(shù)。但是,當(dāng)高級(jí)語(yǔ)言被編譯成計(jì)算機(jī)可以識(shí)別的機(jī)器碼時(shí),有一個(gè)問(wèn)題就凸現(xiàn)出來(lái):在CPU中,計(jì)算機(jī)沒(méi)有辦法知道一個(gè)函數(shù)調(diào)用需要多少個(gè)、什么樣的參數(shù),也沒(méi)有硬件可以保存這些參數(shù)。也就是說(shuō),計(jì)算機(jī)不知道怎么給這個(gè)函數(shù)傳遞參數(shù),傳遞參數(shù)的工作必須由函數(shù)調(diào)用者和函數(shù)本身來(lái)協(xié)調(diào)。為此,計(jì)算機(jī)提供了一種被稱為棧的數(shù)據(jù)結(jié)構(gòu)來(lái)支持參數(shù)傳遞。

棧是一種先進(jìn)后出的數(shù)據(jù)結(jié)構(gòu),棧有一個(gè)存儲(chǔ)區(qū)、一個(gè)棧頂指針。棧頂指針指向堆棧中第一個(gè)可用的數(shù)據(jù)項(xiàng)(被稱為棧頂)。用戶可以在棧頂上方向棧中加入數(shù)據(jù),這個(gè)操作被稱為壓棧(Push),壓棧以后,棧頂自動(dòng)變成新加入數(shù)據(jù)項(xiàng)的位置,棧頂指針也隨之修改。用戶也可以從堆棧中取走棧頂,稱為彈出棧(pop),彈出棧后,棧頂下的一個(gè)元素變成棧頂,棧頂指針隨之修改。

函數(shù)調(diào)用時(shí),調(diào)用者依次把參數(shù)壓棧,然后調(diào)用函數(shù),函數(shù)被調(diào)用以后,在堆棧中取得數(shù)據(jù),并進(jìn)行計(jì)算。函數(shù)計(jì)算結(jié)束以后,或者調(diào)用者、或者函數(shù)本身修改堆棧,使堆棧恢復(fù)原裝。

在參數(shù)傳遞中,有兩個(gè)很重要的問(wèn)題必須得到明確說(shuō)明:

  • 當(dāng)參數(shù)個(gè)數(shù)多于一個(gè)時(shí),按照什么順序把參數(shù)壓入堆棧
  • 函數(shù)調(diào)用后,由誰(shuí)來(lái)把堆棧恢復(fù)原裝

在高級(jí)語(yǔ)言中,通過(guò)函數(shù)調(diào)用約定來(lái)說(shuō)明這兩個(gè)問(wèn)題。常見(jiàn)的調(diào)用約定有:

  • stdcall
  • cdecl
  • fastcall
  • thiscall
  • naked call

stdcall調(diào)用約定

stdcall很多時(shí)候被稱為pascal調(diào)用約定,因?yàn)閜ascal是早期很常見(jiàn)的一種教學(xué)用計(jì)算機(jī)程序設(shè)計(jì)語(yǔ)言,其語(yǔ)法嚴(yán)謹(jǐn),使用的函數(shù)調(diào)用約定就是stdcall。在Microsoft C++系列的C/C++編譯器中,常常用PASCAL宏來(lái)聲明這個(gè)調(diào)用約定,類(lèi)似的宏還有WINAPI和CALLBACK。

stdcall調(diào)用約定聲明的語(yǔ)法為(以前文的那個(gè)函數(shù)為例):

int __stdcall function(int a,int b)

stdcall的調(diào)用約定意味著:1)參數(shù)從右向左壓入堆棧,2)函數(shù)自身修改堆棧 3)函數(shù)名自動(dòng)加前導(dǎo)的下劃線,后面緊跟一個(gè)@符號(hào),其后緊跟著參數(shù)的尺寸

以上述這個(gè)函數(shù)為例,參數(shù)b首先被壓棧,然后是參數(shù)a,函數(shù)調(diào)用function(1,2)調(diào)用處翻譯成匯編語(yǔ)言將變成:

push 2 第二個(gè)參數(shù)入棧 push 1 第一個(gè)參數(shù)入棧 call function 調(diào)用參數(shù),注意此時(shí)自動(dòng)把cs:eip入棧

而對(duì)于函數(shù)自身,則可以翻譯為:

push ebp 保存ebp寄存器,該寄存器將用來(lái)保存堆棧的棧頂指針,可以在函數(shù)退出時(shí)恢復(fù) mov ebp,esp 保存堆棧指針 mov eax,[ebp + 8H] 堆棧中ebp指向位置之前依次保存有ebp,cs:eip,a,b,ebp +8指向a add eax,[ebp + 0CH] 堆棧中ebp + 12處保存了b mov esp,ebp 恢復(fù)esp pop ebp ret 8

而在編譯時(shí),這個(gè)函數(shù)的名字被翻譯成_function@8

注意不同編譯器會(huì)插入自己的匯編代碼以提供編譯的通用性,但是大體代碼如此。其中在函數(shù)開(kāi)始處保留esp到ebp中,在函數(shù)結(jié)束恢復(fù)是編譯器常用的方法。

從函數(shù)調(diào)用看,2和1依次被push進(jìn)堆棧,而在函數(shù)中又通過(guò)相對(duì)于ebp(即剛進(jìn)函數(shù)時(shí)的堆棧指針)的偏移量存取參數(shù)。函數(shù)結(jié)束后,ret 8表示清理8個(gè)字節(jié)的堆棧,函數(shù)自己恢復(fù)了堆棧。

cdecl調(diào)用約定

cdecl調(diào)用約定又稱為C調(diào)用約定,是C語(yǔ)言缺省的調(diào)用約定,它的定義語(yǔ)法是:

int function (int a ,int b) //不加修飾就是C調(diào)用約定 int __cdecl function(int a,int b)//明確指出C調(diào)用約定

在寫(xiě)本文時(shí),出乎我的意料,發(fā)現(xiàn)cdecl調(diào)用約定的參數(shù)壓棧順序是和stdcall是一樣的,參數(shù)首先由有向左壓入堆棧。所不同的是,函數(shù)本身不清理堆棧,調(diào)用者負(fù)責(zé)清理堆棧。由于這種變化,C調(diào)用約定允許函數(shù)的參數(shù)的個(gè)數(shù)是不固定的,這也是C語(yǔ)言的一大特色。對(duì)于前面的function函數(shù),使用cdecl后的匯編碼變成:

調(diào)用處 push 1 push 2 call function add esp,8 注意:這里調(diào)用者在恢復(fù)堆棧被調(diào)用函數(shù)_function處 push ebp 保存ebp寄存器,該寄存器將用來(lái)保存堆棧的棧頂指針,可以在函數(shù)退出時(shí)恢復(fù) mov ebp,esp 保存堆棧指針 mov eax,[ebp + 8H] 堆棧中ebp指向位置之前依次保存有ebp,cs:eip,a,b,ebp +8指向a add eax,[ebp + 0CH] 堆棧中ebp + 12處保存了b mov esp,ebp 恢復(fù)esp pop ebp ret 注意,這里沒(méi)有修改堆棧

MSDN中說(shuō),該修飾自動(dòng)在函數(shù)名前加前導(dǎo)的下劃線,因此函數(shù)名在符號(hào)表中被記錄為_(kāi)function,但是我在編譯時(shí)似乎沒(méi)有看到這種變化。

由于參數(shù)按照從右向左順序壓棧,因此最開(kāi)始的參數(shù)在最接近棧頂?shù)奈恢茫虼水?dāng)采用不定個(gè)數(shù)參數(shù)時(shí),第一個(gè)參數(shù)在棧中的位置肯定能知道,只要不定的參數(shù)個(gè)數(shù)能夠根據(jù)第一個(gè)后者后續(xù)的明確的參數(shù)確定下來(lái),就可以使用不定參數(shù),例如對(duì)于CRT中的sprintf函數(shù),定義為:

int sprintf(char* buffer,const char* format,...)

由于所有的不定參數(shù)都可以通過(guò)format確定,因此使用不定個(gè)數(shù)的參數(shù)是沒(méi)有問(wèn)題的。

fastcall

fastcall調(diào)用約定和stdcall類(lèi)似,它意味著:

  • 函數(shù)的第一個(gè)和第二個(gè)DWORD參數(shù)(或者尺寸更小的)通過(guò)ecx和edx傳遞,其他參數(shù)通過(guò)從右向左的順序壓棧
  • 被調(diào)用函數(shù)清理堆棧
  • 函數(shù)名修改規(guī)則同stdcall

其聲明語(yǔ)法為:int fastcall function(int a,int b)

thiscall

thiscall是唯一一個(gè)不能明確指明的函數(shù)修飾,因?yàn)閠hiscall不是關(guān)鍵字。它是C++類(lèi)成員函數(shù)缺省的調(diào)用約定。由于成員函數(shù)調(diào)用還有一個(gè)this指針,因此必須特殊處理,thiscall意味著:

  • 參數(shù)從右向左入棧
  • 如果參數(shù)個(gè)數(shù)確定,this指針通過(guò)ecx傳遞給被調(diào)用者;如果參數(shù)個(gè)數(shù)不確定,this指針在所有參數(shù)壓棧后被壓入堆棧。
  • 對(duì)參數(shù)個(gè)數(shù)不定的,調(diào)用者清理堆棧,否則函數(shù)自己清理堆棧

為了說(shuō)明這個(gè)調(diào)用約定,定義如下類(lèi)和使用代碼:

class A { public: ?? int function1(int a,int b); ?? int function2(int a,...); }; int A::function1 (int a,int b) { ?? return a+b; } #include  int A::function2(int a,...) { ?? va_list ap; ?? va_start(ap,a); ?? int i; ?? int result = 0; ?? for(i = 0 ; i < a ; i ++) ?? { ???? result += va_arg(ap,int); ?? } ?? return result; } void callee() { ?? A a; ?? a.function1 (1,2); ?? a.function2(3,1,2,3); } 

callee函數(shù)被翻譯成匯編后就變成:

//函數(shù)function1調(diào)用 0401C1D push 2 00401C1F push 1 00401C21 lea ecx,[ebp-8] 00401C24 call function1 注意,這里this沒(méi)有被入棧 //函數(shù)function2調(diào)用 00401C29 push 3 00401C2B push 2 00401C2D push 1 00401C2F push 3 00401C31 lea eax,[ebp-8] 這里引入this指針 00401C34 push eax 00401C35 call function2 00401C3A add esp,14h

可見(jiàn),對(duì)于參數(shù)個(gè)數(shù)固定情況下,它類(lèi)似于stdcall,不定時(shí)則類(lèi)似cdecl

naked call

這是一個(gè)很少見(jiàn)的調(diào)用約定,一般程序設(shè)計(jì)者建議不要使用。編譯器不會(huì)給這種函數(shù)增加初始化和清理代碼,更特殊的是,你不能用return返回返回值,只能用插入?yún)R編返回結(jié)果。這一般用于實(shí)模式驅(qū)動(dòng)程序設(shè)計(jì),假設(shè)定義一個(gè)求和的加法程序,可以定義為:

__declspec(naked) int add(int a,int b) { __asm mov eax,a __asm add eax,b __asm ret } 

注意,這個(gè)函數(shù)沒(méi)有顯式的return返回值,返回通過(guò)修改eax寄存器實(shí)現(xiàn),而且連退出函數(shù)的ret指令都必須顯式插入。上面代碼被翻譯成匯編以后變成:

mov eax,[ebp+8] add eax,[ebp+12] ret 8

注意這個(gè)修飾是和__stdcall及cdecl結(jié)合使用的,前面是它和cdecl結(jié)合使用的代碼,對(duì)于和stdcall結(jié)合的代碼,則變成:

__declspec(naked) int __stdcall function(int a,int b) { __asm mov eax,a __asm add eax,b __asm ret 8 //注意后面的8 } 

至于這種函數(shù)被調(diào)用,則和普通的cdecl及stdcall調(diào)用函數(shù)一致。

函數(shù)調(diào)用約定導(dǎo)致的常見(jiàn)問(wèn)題

如果定義的約定和使用的約定不一致,則將導(dǎo)致堆棧被破壞,導(dǎo)致嚴(yán)重問(wèn)題,下面是兩種常見(jiàn)的問(wèn)題:

  1. 函數(shù)原型聲明和函數(shù)體定義不一致
  2. DLL導(dǎo)入函數(shù)時(shí)聲明了不同的函數(shù)約定

以后者為例,假設(shè)我們?cè)赿ll種聲明了一種函數(shù)為:

__declspec(dllexport) int func(int a,int b);//注意,這里沒(méi)有stdcall,使用的是cdecl

使用時(shí)代碼為:

 typedef int (*WINAPI DLLFUNC)func(int a,int b); hLib = LoadLibrary(...); DLLFUNC func = (DLLFUNC)GetProcAddress(...)//這里修改了調(diào)用約定 result = func(1,2);//導(dǎo)致錯(cuò)誤 

由于調(diào)用者沒(méi)有理解WINAPI的含義錯(cuò)誤的增加了這個(gè)修飾,上述代碼必然導(dǎo)致堆棧被破壞,MFC在編譯時(shí)插入的checkesp函數(shù)將告訴你,堆棧被破壞了。

posted @ 2006-07-17 10:06 逃逃 閱讀(467) | 評(píng)論 (1)編輯 收藏


2006年5月22日

1、文件操作的方法

???使用Visual C++編程,有如下方法進(jìn)行文件操作:

(1)使用標(biāo)準(zhǔn)C運(yùn)行庫(kù)函數(shù),包括fopen、fclose、fseek等。

???(2)使用Win16下的文件和目錄操作函數(shù),如lopen、lclose、lseek等。不過(guò),在Win32下,這些函數(shù)主要是為了和Win16向后兼容。

???(3)使用Win32下的文件和目錄操作函數(shù),如CreateFile,CopyFile,DeleteFile,F(xiàn)indNextFile,等等。

??????Win32下,打開(kāi)和創(chuàng)建文件都由CreateFile完成,成功的話,得到一個(gè)Win32下的句柄,這不同于“C”的fopen返回的句柄。在Win16下,該句柄和C運(yùn)行庫(kù)文件操作函數(shù)相容。但在Win32下,“C”的文件操作函數(shù)不能使用該句柄,如果需要的話,可以使用函數(shù)_open_osfhandle從Win32句柄得到一個(gè)“C”文件函數(shù)可以使用的文件句柄。 關(guān)閉文件使用Win32的CloseHandle。 在Win32下,CreateFile可以操作的對(duì)象除了磁盤(pán)文件外,還包括設(shè)備文件如通訊端口、管道、控制臺(tái)輸入、郵件槽等等。

(4)使用CFile和其派生類(lèi)進(jìn)行文件操作。CFile從CObject派生,其派生類(lèi)包括操作文本文件的CStdioFile,操作內(nèi)存文件的CmemFile,等等。CFile是建立在Win32的文件操作體系的基礎(chǔ)上,它封裝了部分Win32文件操作函數(shù)。最好是使用CFile類(lèi)(或派生類(lèi))的對(duì)象來(lái)操作文件,必要的話,可以從這些類(lèi)派生自己的文件操作類(lèi)。統(tǒng)一使用CFile的界面可以得到好的移植性。


2、文件操作的方法
MFC用一些類(lèi)來(lái)封裝文件訪問(wèn)的Win32 API。以CFile為基礎(chǔ),從CFile派生出幾個(gè)類(lèi),如CStdioFile,CMemFile,MFC內(nèi)部使用的CMiororFile,等等。

2.1、CFile的結(jié)構(gòu)

?????2.1.1、CFile定義的枚舉類(lèi)型

???????????????CFile類(lèi)定義了一些和文件操作相關(guān)的枚舉類(lèi)型,主要有四種:OpenFlags,Attribute,SeekPosition,hFileNull。下面,分別解釋這些枚舉類(lèi)型。

  1. OpenFlags

    OpenFlags定義了13種文件訪問(wèn)和共享模式:

    enum OpenFlags {

    //第一(從右,下同)至第二位,打開(kāi)文件時(shí)訪問(wèn)模式,讀/寫(xiě)/讀寫(xiě)

    modeRead = 0x0000,

    modeWrite = 0x0001,

    modeReadWrite = 0x0002,

    shareCompat = 0x0000, //32位MFC中沒(méi)用

    //第五到第七位,打開(kāi)文件時(shí)的共享模式

    shareExclusive = 0x0010,//獨(dú)占方式,禁止其他進(jìn)程讀寫(xiě)

    shareDenyWrite = 0x0020,//禁止其他進(jìn)程寫(xiě)

    shareDenyRead = 0x0030,//禁止其他進(jìn)程讀

    shareDenyNone = 0x0040,//允許其他進(jìn)程寫(xiě)

    //第八位,打開(kāi)文件時(shí)的文件繼承方式

    modeNoInherit = 0x0080,//不允許子進(jìn)程繼承

    //第十三、十四位,是否創(chuàng)建新文件和創(chuàng)建方式

    modeCreate = 0x1000,//創(chuàng)建新文件,文件長(zhǎng)度0

    modeNoTruncate = 0x2000,//創(chuàng)建新文件時(shí)如文件已存在則打開(kāi)

    //第十五、十六位,文件以二進(jìn)制或者文本方式打開(kāi),在派生類(lèi)CStdioFile中用

    typeText = 0x4000,

    typeBinary = (int)0x8000

    };

  2. Attribute

    Attribute定義了文件屬性:正常、只讀、隱含、系統(tǒng)文件,文件或者目錄等。

    enum Attribute {

    normal = 0x00,

    readOnly = 0x01,

    hidden = 0x02,

    system = 0x04,

    volume = 0x08,

    directory = 0x10,

    archive = 0x20

    }

  3. SeekPosition

    SeekPosition定義了三種文件位置:頭、尾、當(dāng)前:

    enum SeekPosition{

    begin = 0x0,

    current = 0x1,

    end = 0x2

    };

  4. hFileNull

hFileNull定義了空文件句柄

enum { hFileNull = -1 };

2.1.2、CFile的其他一些成員變量

CFile除了定義枚舉類(lèi)型,還定義了一些成員變量。例如:

UINT m_hFile

該成員變量是public訪問(wèn)屬性,保存::CreateFile返回的操作系統(tǒng)的文件句柄。MFC重載了運(yùn)算符號(hào)HFILE來(lái)返回m_hFile,這樣在使用HFILE類(lèi)型變量的地方可以使用CFile對(duì)象。

BOOL m_bCloseOnDelete;

CString m_strFileName;

這兩個(gè)成員變量是protected訪問(wèn)屬性。m_bCloseOnDelete用來(lái)指示是否在關(guān)閉文件時(shí)刪除CFile對(duì)象;m_strFileName用來(lái)保存文件名。

2.1.3、CFile的成員函數(shù)

CFile的成員函數(shù)實(shí)現(xiàn)了對(duì)Win32文件操作函數(shù)的封裝,完成以下動(dòng)作:打開(kāi)、創(chuàng)建、關(guān)閉文件,文件指針定位,文件的鎖定與解鎖,文件狀態(tài)的讀取和修改,等等。其中,用到了m_hFile文件句柄的一般是虛擬函數(shù),和此無(wú)關(guān)的一般是靜態(tài)成員函數(shù)。一般地,成員函數(shù)被映射到對(duì)應(yīng)的Win32函數(shù),如表11-1所示。

表11-1 CFile函數(shù)對(duì)Win32文件函數(shù)的封裝

虛擬

靜態(tài)

成員函數(shù)

對(duì)應(yīng)的Win32函數(shù)

文件的創(chuàng)建、打開(kāi)、關(guān)閉

 

Abort

CloseHandle

 

Duplicate

DuplicateHandle

 

Open

CreateFile

 

Close

CloseHandle

文件的讀寫(xiě)

 

Read

ReadFile

   

ReadHuge(向后兼容)

調(diào)用Read成員函數(shù)

 

Write

WriteFile

   

WriteHuage(向后兼容)

調(diào)用Write成員函數(shù)

 

Flush

FlushFileBuffers

文件定位

 

Seek

SetFilePointer

   

SeekToBegin

調(diào)用Seek成員函數(shù)

   

SeekToEnd

調(diào)用Seek成員函數(shù)

 

GetLength

調(diào)用Seek成員函數(shù)

 

SetLength

SetEndOfFile

文件的鎖定/解鎖

 

LockRange

LockFile

 

UnlockRange

UnlockFile

文件狀態(tài)操作函數(shù)

 

GetPosition

SetFilePointer

   

GetStatus(CFileStatus&)

GetFileTime,GetFileSize等

 

GetStatus(LPSTR lpszFileName CFileStatus&)

FindFirstFile

 

GetFileName

不是簡(jiǎn)單地映射到某個(gè)函數(shù)

 

GetFileTitle

 

 

GetFilePath

 

 

SetFilePath

 
 

SetStatus

 

改名和刪除

 

Rename

MoveFile

 

Remove

DeleteFile


2.1.4、CFile的部分實(shí)現(xiàn)


這里主要討論CFile對(duì)象的構(gòu)造函數(shù)和文件的打開(kāi)/創(chuàng)建的過(guò)程。

  1. 構(gòu)造函數(shù)

CFile有如下幾個(gè)構(gòu)造函數(shù):

  • CFile()

缺省構(gòu)造函數(shù),僅僅構(gòu)造一個(gè)CFile對(duì)象,還必須使用Open成員函數(shù)來(lái)打開(kāi)文件。

  • CFile(int hFile)

已經(jīng)打開(kāi)了一個(gè)文件hFile,在此基礎(chǔ)上構(gòu)造一個(gè)CFile對(duì)象來(lái)給它打包。HFile將被賦值給CFile的成員變量m_hFile。

  • CFile(LPCTSTR lpszFileName, UINT nOpenFlags)

指定一個(gè)文件名和文件打開(kāi)方式,構(gòu)造CFile對(duì)象,調(diào)用Open打開(kāi)/創(chuàng)建文件,把文件句柄保存到m_hFile。

  1. 打開(kāi)/創(chuàng)建文件

Open的原型如下:

BOOL CFile::Open(LPCTSTR lpszFileName, UINT nOpenFlags,

CFileException* pException)

Open調(diào)用Win32函數(shù)::CreateFile打開(kāi)文件,并把文件句柄保存到成員變量m_hFile中。

CreateFile函數(shù)的原型如下:

HANDLE CreateFile(

LPCTSTR lpFileName,// pointer to name of the file

DWORD dwDesiredAccess,// access (read-write) mode

DWORD dwShareMode,// share mode

LPSECURITY_ATTRIBUTES lpSecurityAttributes, //pointer to security descriptor

DWORD dwCreationDistribution,// how to create

DWORD dwFlagsAndAttributes,// file attributes

HANDLE hTemplateFile// handle to file with attributes to copy

);

顯然,Open必須把自己的兩個(gè)參數(shù)lpszFileName和nOpenFlags映射到CreateFile的七個(gè)參數(shù)上。

從OpenFlags的定義可以看出,(nOpenFlags & 3)表示了讀寫(xiě)標(biāo)識(shí),映射成變量dwAccess,可以取值為Win32的GENERIC_READ、GENERIC_WRITE、GENERIC_READ|GENERIC_WRITE。

(nOpenFlags & 0x70)表示了共享模式,映射成變量dwShareMode,可以取值為Win32的FILE_SHARE_READ、FILE_SHARE_WRITE、FILE_SHARE_WRITE|FILE_SHARE_READ。

Open定義了一個(gè)局部的SECURITY_ATTRIBUTES變量sa,(nOpenFlags & 0x80)被賦值給sa.bInheritHandle。

(nOpenFlags & modeCreate)表示了創(chuàng)建方式,映射成變量dwCreateFlag,可以取值為Win32的OPEN_ALWAYS、CREATE_ALWAYS、OPEN_EXISTING。

在生成了上述參數(shù)之后,先調(diào)用::CreateFile:

HANDLE hFile =::CreateFile(lpszFileName,

dwAccess, dwShareMode, &sa,

dwCreateFlag, FILE_ATTRIBUTE_NORMAL, NULL);

然后,hFile被賦值給成員變量m_hFile,m_bCloseOnDelete被設(shè)置為T(mén)RUE。

由上可以看出,CFile打開(kāi)(創(chuàng)建)一個(gè)文件時(shí)大大簡(jiǎn)化了:: CreateFile函數(shù)的復(fù)雜性,即只需要指定一個(gè)文件名、一個(gè)打開(kāi)文件的參數(shù)即可。若該參數(shù)指定為0,則表示以只讀方式打開(kāi)一個(gè)存在的文件,獨(dú)占使用,不允許子進(jìn)程繼承。

在CFile對(duì)象使用時(shí),如果它是在堆中分配的,則應(yīng)該銷(xiāo)毀它;如果在棧中分配的,則CFile對(duì)象將被自動(dòng)銷(xiāo)毀。銷(xiāo)毀時(shí)析構(gòu)函數(shù)被調(diào)用,析構(gòu)函數(shù)是虛擬函數(shù)。若m_bCloseOnDelete為真且m_hFile非空,則析構(gòu)函數(shù)調(diào)用Close關(guān)閉文件。

至于其他CFile成員函數(shù)的實(shí)現(xiàn),這里不作分析了。

2.1.5、CFile的派生類(lèi)

這里主要簡(jiǎn)要地介紹CStdioFile和CmemFile及CFileFind。

  1. CStdioFile

    CStdioFile對(duì)文本文件進(jìn)行操作。

    CStdioFile定義了新的成員變量m_pStream,類(lèi)型是FILE*。在打開(kāi)或者創(chuàng)建文件時(shí),使用_open_osfhandle從m_hFile(Win32文件句柄)得到一個(gè)“C”的FILE類(lèi)型的文件指針,然后,在文件操作中,使用“C”的文件操作函數(shù)。例如,讀文件使用_fread,而不是::ReadFile,寫(xiě)文件使用了_fwrite,而不是::WriteFile,等等。m_hFile是CFile的成員變量。

    另外,CStdioFile不支持CFile的Dumplicate、LockRange、UnlockRange操作,但是實(shí)現(xiàn)了兩個(gè)新的操作ReadString和WriteString。

  2. CMemFile

    CMemFile把一塊內(nèi)存當(dāng)作一個(gè)文件來(lái)操作,所以,它沒(méi)有打開(kāi)文件的操作,而是設(shè)計(jì)了Attach和Detach用來(lái)分配或者釋放一塊內(nèi)存。相應(yīng)地,它提供了Alloc、Free虛擬函數(shù)來(lái)操作內(nèi)存文件,它覆蓋了Read、Write來(lái)讀寫(xiě)內(nèi)存文件。

  3. CFileFind

為了方便文件查找,MFC把有關(guān)功能歸結(jié)成為一個(gè)類(lèi)CFileFind。CFileFind派生于CObject類(lèi)。首先,它使用FindFile和FineNextFile包裝了Win32函數(shù)::FindFirstFile和::FindNextFile;其次,它提供了許多函數(shù)用來(lái)獲取文件的狀態(tài)或者屬性。

使用CFileStatus結(jié)構(gòu)來(lái)描述文件的屬性,其定義如下:

struct CFileStatus

{

CTime m_ctime; // 文件創(chuàng)建時(shí)間

CTime m_mtime; // 文件最近一次修改時(shí)間

CTime m_atime; // 文件最近一次訪問(wèn)時(shí)間

LONG m_size; // 文件大小

BYTE m_attribute; // 文件屬性

BYTE _m_padding; // 沒(méi)有實(shí)際含義,用來(lái)增加一個(gè)字節(jié)

TCHAR m_szFullName[_MAX_PATH]; //絕對(duì)路徑

#ifdef _DEBUG

//實(shí)現(xiàn)Dump虛擬函數(shù),輸出文件屬性

void Dump(CDumpContext& dc) const;

#endif

};

例如:

CFileStatus status;

pFile->GetStatus(status);

#ifdef _DEBUG

status.dump(afxDump);

#endif

 

posted @ 2006-05-22 16:13 逃逃 閱讀(689) | 評(píng)論 (1)編輯 收藏


2006年4月17日

10.1內(nèi)存分配?
??10.1.1?內(nèi)存分配函數(shù)

MFCWin32或者C語(yǔ)言的內(nèi)存分配API,有四種內(nèi)存分配API可供使用。

  1. Win32的堆分配函數(shù)

    每一個(gè)進(jìn)程都可以使用堆分配函數(shù)創(chuàng)建一個(gè)私有的堆──調(diào)用進(jìn)程地址空間的一個(gè)或者多個(gè)頁(yè)面。DLL創(chuàng)建的私有堆必定在調(diào)用DLL的進(jìn)程的地址空間內(nèi),只能被調(diào)用進(jìn)程訪問(wèn)。

    HeapCreate用來(lái)創(chuàng)建堆;HeapAlloc用來(lái)從堆中分配一定數(shù)量的空間,HeapAlloc分配的內(nèi)存是不能移動(dòng)的;HeapSize可以確定從堆中分配的空間的大小;HeapFree用來(lái)釋放從堆中分配的空間;HeapDestroy銷(xiāo)毀創(chuàng)建的堆。

  2. Windows傳統(tǒng)的全局或者局部?jī)?nèi)存分配函數(shù)

    由于Win32采用平面內(nèi)存結(jié)構(gòu)模式,Win32下的全局和局部?jī)?nèi)存函數(shù)除了名字不同外,其他完全相同。任一函數(shù)都可以用來(lái)分配任意大小的內(nèi)存(僅僅受可用物理內(nèi)存的限制)。用法可以和Win16下基本一樣。

    Win32下保留這類(lèi)函數(shù)保證了和Win16的兼容。

  3. C語(yǔ)言的標(biāo)準(zhǔn)內(nèi)存分配函數(shù)

    C語(yǔ)言的標(biāo)準(zhǔn)內(nèi)存分配函數(shù)包括以下函數(shù):

    malloc,calloc,realloc,free,等。

    這些函數(shù)最后都映射成堆API函數(shù),所以,malloc分配的內(nèi)存是不能移動(dòng)的。這些函數(shù)的調(diào)式版本為

    malloc_dbg,calloc_dbg,realloc_dbg,free_dbg,等。

  4. Win32的虛擬內(nèi)存分配函數(shù)

虛擬內(nèi)存API是其他API的基礎(chǔ)。虛擬內(nèi)存API以頁(yè)為最小分配單位,X86上頁(yè)長(zhǎng)度為4KB,可以用GetSystemInfo函數(shù)提取頁(yè)長(zhǎng)度。虛擬內(nèi)存分配函數(shù)包括以下函數(shù):

  • LPVOID VirtualAlloc(LPVOID lpvAddress,

DWORD cbSize,

DWORD fdwAllocationType,

DWORD fdwProtect);

該函數(shù)用來(lái)分配一定范圍的虛擬頁(yè)。參數(shù)1指定起始地址;參數(shù)2指定分配內(nèi)存的長(zhǎng)度;參數(shù)3指定分配方式,取值MEM_COMMINT或者M(jìn)EM_RESERVE;參數(shù)4指定控制訪問(wèn)本次分配的內(nèi)存的標(biāo)識(shí),取值為PAGE_READONLY、PAGE_READWRITE或者PAGE_NOACCESS。

  • LPVOID VirtualAllocEx(HANDLE process,

LPVOID lpvAddress,

DWORD cbSize,

DWORD fdwAllocationType,

DWORD fdwProtect);

該函數(shù)功能類(lèi)似于VirtualAlloc,但是允許指定進(jìn)程process。VirtaulFree、VirtualProtect、VirtualQuery都有對(duì)應(yīng)的擴(kuò)展函數(shù)。

  • BOOL VirtualFree(LPVOID lpvAddress,

DWORD dwSize,

DWORD dwFreeType);

該函數(shù)用來(lái)回收或者釋放分配的虛擬內(nèi)存。參數(shù)1指定希望回收或者釋放內(nèi)存的基地址;如果是回收,參數(shù)2可以指向虛擬地址范圍內(nèi)的任何地方,如果是釋放,參數(shù)2必須是VirtualAlloc返回的地址;參數(shù)3指定是否釋放或者回收內(nèi)存,取值為MEM_DECOMMINT或者M(jìn)EM_RELEASE。

  • BOOL VirtualProtect(LPVOID lpvAddress,

DWORD cbSize,

DWORD fdwNewProtect,

PDWORD pfdwOldProtect);

該函數(shù)用來(lái)把已經(jīng)分配的頁(yè)改變成保護(hù)頁(yè)。參數(shù)1指定分配頁(yè)的基地址;參數(shù)2指定保護(hù)頁(yè)的長(zhǎng)度;參數(shù)3指定頁(yè)的保護(hù)屬性,取值PAGE_READ、PAGE_WRITE、PAGE_READWRITE等等;參數(shù)4用來(lái)返回原來(lái)的保護(hù)屬性。

  • DWORD VirtualQuery(LPCVOID lpAddress,

PMEMORY_BASIC_INFORMATION lpBuffer,

DWORD dwLength

);

該函數(shù)用來(lái)查詢內(nèi)存中指定頁(yè)的特性。參數(shù)1指向希望查詢的虛擬地址;參數(shù)2是指向內(nèi)存基本信息結(jié)構(gòu)的指針;參數(shù)3指定查詢的長(zhǎng)度。

  • BOOL VirtualLock(LPVOID lpAddress,DWORD dwSize);

該函數(shù)用來(lái)鎖定內(nèi)存,鎖定的內(nèi)存頁(yè)不能交換到頁(yè)文件。參數(shù)1指定要鎖定內(nèi)存的起始地址;參數(shù)2指定鎖定的長(zhǎng)度。

  • BOOL VirtualUnLock(LPVOID lpAddress,DWORD dwSize);

參數(shù)1指定要解鎖的內(nèi)存的起始地址;參數(shù)2指定要解鎖的內(nèi)存的長(zhǎng)度。


C++的new 和 delete操作符

MFC定義了兩種作用范圍的new和delete操作符。對(duì)于new,不論哪種,參數(shù)1類(lèi)型必須是size_t,且返回void類(lèi)型指針。

  1. 全局范圍內(nèi)的new和delete操作符

    原型如下:

    void _cdecl ::operator new(size_t nSize);

    void __cdecl operator delete(void* p);

    調(diào)試版本:

    void* __cdecl operator new(size_t nSize, int nType,

    LPCSTR lpszFileName, int nLine)

  2. 類(lèi)定義的new和delete操作符

原型如下:

void* PASCAL classname::operator new(size_t nSize);

void PASCAL classname::operator delete(void* p);

類(lèi)的operator new操作符是類(lèi)的靜態(tài)成員函數(shù),對(duì)該類(lèi)的對(duì)象來(lái)說(shuō)將覆蓋全局的operator new。全局的operator new用來(lái)給內(nèi)部類(lèi)型對(duì)象(如int)、沒(méi)有定義operator new操作符的類(lèi)的對(duì)象分配內(nèi)存。

new操作符被映射成malloc或者malloc_dbg,delete被映射成free或者free_dbg。



10.1.2調(diào)試手段

MFC應(yīng)用程序可以使用C運(yùn)行庫(kù)的調(diào)試手段,也可以使用MFC提供的調(diào)試手段。兩種調(diào)試手段分別論述如下。

  1. C運(yùn)行庫(kù)提供和支持的調(diào)試功能

    C運(yùn)行庫(kù)提供和支持的調(diào)試功能如下:

    1. 調(diào)試信息報(bào)告函數(shù)

      用來(lái)報(bào)告應(yīng)用程序的調(diào)試版本運(yùn)行時(shí)的警告和出錯(cuò)信息。包括:

      _CrtDbgReport 用來(lái)報(bào)告調(diào)試信息;

      _CrtSetReportMode 設(shè)置是否警告、出錯(cuò)或者斷言信息;

      _CrtSetReportFile 設(shè)置是否把調(diào)試信息寫(xiě)入到一個(gè)文件。

    2. 條件驗(yàn)證或者斷言宏:

      斷言宏主要有:

      assert 檢驗(yàn)?zāi)硞€(gè)條件是否滿足,不滿足終止程序執(zhí)行。

      驗(yàn)證函數(shù)主要有:

      _CrtIsValidHeapPointer 驗(yàn)證某個(gè)指針是否在本地堆中;

      _CrtIsValidPointer 驗(yàn)證指定范圍的內(nèi)存是否可以讀寫(xiě);

      _CrtIsMemoryBlock 驗(yàn)證某個(gè)內(nèi)存塊是否在本地堆中。

    3. 內(nèi)存(堆)調(diào)試:

    malloc_dbg 分配內(nèi)存時(shí)保存有關(guān)內(nèi)存分配的信息,如在什么文件、哪一行分配的內(nèi)存等。有一系列用來(lái)提供內(nèi)存診斷的函數(shù):

    _CrtMemCheckpoint 保存內(nèi)存快照在一個(gè)_CrtMemState結(jié)構(gòu)中;

    _CrtMemDifference 比較兩個(gè)_CrtMemState;

    _CrtMemDumpStatistics 轉(zhuǎn)儲(chǔ)輸出一_CrtMemState結(jié)構(gòu)的內(nèi)容;

    _CrtMemDumpAllObjectsSince 輸出上次快照或程序開(kāi)始執(zhí)行以來(lái)在堆中分配的所有對(duì)象的信息;

    _CrtDumpMemoryLeaks 檢測(cè)程序執(zhí)行以來(lái)的內(nèi)存漏洞,如果有漏洞則輸出所有分配的對(duì)象。

    2.????? MFC提供的調(diào)試手段

    MFC在C運(yùn)行庫(kù)提供和支持的調(diào)試功能基礎(chǔ)上,設(shè)計(jì)了一些類(lèi)、函數(shù)等來(lái)協(xié)助調(diào)試。

    1. MFC的TRACE、ASSERT

      ASSERT

      使用ASSERT斷言判定程序是否可以繼續(xù)執(zhí)行。

      TRACE

      使用TRACE宏顯示或者打印調(diào)試信息。TRACE是通過(guò)函數(shù)AfxTrace實(shí)現(xiàn)的。由于AfxTrace函數(shù)使用了cdecl調(diào)用約定,故可以接受個(gè)數(shù)不定的參數(shù),如同printf函數(shù)一樣。它的定義和實(shí)現(xiàn)如下:

      void AFX_CDECL AfxTrace(LPCTSTR lpszFormat, ...)

      {

      #ifdef _DEBUG // all AfxTrace output is controlled by afxTraceEnabled

      if (!afxTraceEnabled)

      return;

      #endif

      //處理個(gè)數(shù)不定的參數(shù)

      va_list args;

      va_start(args, lpszFormat);

      int nBuf;

      TCHAR szBuffer[512];

      nBuf = _vstprintf(szBuffer, lpszFormat, args);

      ASSERT(nBuf < _countof(szBuffer));

      if ((afxTraceFlags & traceMultiApp) && (AfxGetApp() != NULL))

      afxDump << AfxGetApp()->m_pszExeName << ": ";

      afxDump << szBuffer;

      va_end(args);

      }

      #endif //_DEBUG

      在程序源碼中,可以控制是否顯示跟蹤信息,顯示什么跟蹤信息。如果全局變量afxTraceEnabled為T(mén)RUE,則TRACE宏可以輸出;否則,沒(méi)有TRACE信息被輸出。如果通過(guò)afxTraceFlags指定了跟蹤什么消息,則輸出有關(guān)跟蹤信息,例如為了指定“Multilple Application Debug”,令A(yù)fxTraceFlags|=traceMultiApp。可以跟蹤的信息有:

      enum AfxTraceFlags

      {

      traceMultiApp = 1, // multi-app debugging

      traceAppMsg = 2, // main message pump trace (includes DDE)

      traceWinMsg = 4, // Windows message tracing

      traceCmdRouting = 8, // Windows command routing trace

      //(set 4+8 for control notifications)

      traceOle = 16, // special OLE callback trace

      traceDatabase = 32, // special database trace

      traceInternet = 64 // special Internet client trace

      };

      這樣,應(yīng)用程序可以在需要的地方指定afxTraceEnabled的值打開(kāi)或者關(guān)閉TRACE開(kāi)關(guān),指定AfxTraceFlags的值過(guò)濾跟蹤信息。

      Visual C++提供了一個(gè)TRACE工具,也可以用來(lái)完成上述功能。

      為了顯示消息信息,MFC內(nèi)部定義了一個(gè)AFX_MAP_MESSAG類(lèi)型的數(shù)組allMessages,儲(chǔ)存了Windows消息和消息名映射對(duì)。例如:

      allMessages[1].nMsg = WM_CREATE,

      allMessages[1].lpszMsg = “WM_CREATE”

      MFC內(nèi)部還使用函數(shù)_AfxTraceMsg顯示跟蹤消息,它可以接收一個(gè)字符串和一個(gè)MSG指針,然后,把該字符串和MSG的各個(gè)域的信息組合成一個(gè)大的字符串并使用AfxTrace顯示出來(lái)。

      allMessages和函數(shù)_AfxTraceMsg的詳細(xì)實(shí)現(xiàn)可以參見(jiàn)AfxTrace.cpp。

    2. MFC對(duì)象內(nèi)容轉(zhuǎn)儲(chǔ)

      對(duì)象內(nèi)容轉(zhuǎn)儲(chǔ)是CObject類(lèi)提供的功能,所有從它派生的類(lèi)都可以通過(guò)覆蓋虛擬函數(shù)DUMP來(lái)支持該功能。在講述CObject類(lèi)時(shí)曾提到過(guò)。

      虛擬函數(shù)Dump的定義:

      class ClassName : public CObject

      {

      public:

      #ifdef _DEBUG

      virtual void Dump( CDumpContext& dc ) const;

      #endif

      };

      在使用Dump時(shí),必須給它提供一個(gè)CDumpContext類(lèi)型的參數(shù),該參數(shù)指定的對(duì)象將負(fù)責(zé)輸出調(diào)試信息。為此,MFC提供了一個(gè)預(yù)定義的全局CDumpContext對(duì)象afxDump,它把調(diào)試信息輸送給調(diào)試器的調(diào)試窗口。從前面AfxTrace的實(shí)現(xiàn)可以知道,MFC使用了afxDump輸出跟蹤信息到調(diào)試窗口。

      CDumpContext類(lèi)沒(méi)有基類(lèi),它提供了以文本形式輸出診斷信息的功能。

      例如:

      CPerson* pMyPerson = new CPerson;

      // set some fields of the CPerson object...

      //...

      // now dump the contents

      #ifdef _DEBUG

      pMyPerson->Dump( afxDump );

      #endif

    3. MFC對(duì)象有效性檢測(cè)

    對(duì)象有效性檢測(cè)是CObject類(lèi)提供的功能,所有從它派生的類(lèi)都可以通過(guò)覆蓋虛擬函數(shù)AssertValid來(lái)支持該功能。在講述CObject類(lèi)時(shí)曾提到過(guò)。

    虛擬函數(shù)AssertValid的定義:

    class ClassName : public CObject

    {

    public:

    #ifdef _DEBUG

    virtual void AssertValid( ) const;

    #endif

    };

    使用ASSERT_VALID宏判斷一個(gè)對(duì)象是否有效,該對(duì)象的類(lèi)必須覆蓋了AssertValid函數(shù)。形式為:ASSERT_VALID(pObject)。

    另外,MFC提供了一些函數(shù)來(lái)判斷地址是否有效,如:

    AfxIsMemoryBlock,AfxIsString,AfxIsValidAddress。

    10.1.3內(nèi)存診斷

    MFC使用DEBUG_NEW來(lái)跟蹤內(nèi)存分配時(shí)的執(zhí)行的源碼文件和行數(shù)。

    把#define new DEBUG_NEW插入到每一個(gè)源文件中,這樣,調(diào)試版本就使用_malloc_dbg來(lái)分配內(nèi)存。MFC Appwizard在創(chuàng)建框架文件時(shí)已經(jīng)作了這樣的處理。

    1. AfxDoForAllObjects

      MFC提供了函數(shù)AfxDoForAllObjects來(lái)追蹤動(dòng)態(tài)分配的內(nèi)存對(duì)象,函數(shù)原型如下:

      void AfxDoForAllObjects( void (*pfn)(CObject* pObject,

      void* pContext), void* pContext );

      其中:

      參數(shù)1是一個(gè)函數(shù)指針,AfxDoForAllObjects對(duì)每個(gè)對(duì)象調(diào)用該指針表示的函數(shù)。

      參數(shù)2將傳遞給參數(shù)1指定的函數(shù)。

      AfxDoForAllObjects可以檢測(cè)到所有使用new分配的CObject對(duì)象或者CObject類(lèi)派生的對(duì)象,但全局對(duì)象、嵌入對(duì)象和棧中分配的對(duì)象除外。

    10.1.4內(nèi)存漏洞檢測(cè)

    僅僅用于new的DEBUG版本分配的內(nèi)存。

    完成內(nèi)存漏洞檢測(cè),需要如下系列步驟:

    • 調(diào)用AfxEnableMemoryTracking(TRUE/FALSE)打開(kāi)/關(guān)閉內(nèi)存診斷。在調(diào)試版本下,缺省是打開(kāi)的;關(guān)閉內(nèi)存診斷可以加快程序執(zhí)行速度,減少診斷輸出。

    • 使用MFC全局變量afxMemDF更精確地指定診斷輸出的特征,缺省值是allocMemDF,可以取如下值或者這些值相或:

    afxMemDF,delayFreeMemDF,checkAlwaysMemDF

    其中:allocMemDF表示可以進(jìn)行內(nèi)存診斷輸出;delayFreeMemDF表示是否是在應(yīng)用程序結(jié)束時(shí)才調(diào)用free或者delete,這樣導(dǎo)致程序最大可能的分配內(nèi)存;checkAlwaysMemDF表示每一次分配或者釋放內(nèi)存之后都調(diào)用函數(shù)AfxCheckMemory進(jìn)行內(nèi)存檢測(cè)(AfxCheckMemory檢查堆中所有通過(guò)new分配的內(nèi)存(不含malloc))。

    這一步是可選步驟,非必須。

    • 創(chuàng)建一個(gè)CMemState類(lèi)型的變量oldMemState,調(diào)用CMemState的成員函數(shù)CheckPoint獲得初次內(nèi)存快照。

    • 執(zhí)行了系列內(nèi)存分配或者釋放之后,創(chuàng)建另一個(gè)CMemState類(lèi)型變量newMemState,調(diào)用CMemState的成員函數(shù)CheckPoint獲得新的內(nèi)存快照。

    • 創(chuàng)建第三個(gè)CMemState類(lèi)型變量difMemState,調(diào)用CMemState的成員函數(shù)Difference比較oldMemState和newMemState,結(jié)果保存在變量difMemState中。如果沒(méi)有不同,則返回FALSE,否則返回TRUE。

    • 如果不同,則調(diào)用成員函數(shù)DumpStatistics輸出比較結(jié)果。

    例如:

    // Declare the variables needed

    #ifdef _DEBUG

    CMemoryState oldMemState, newMemState, diffMemState;

    oldMemState.Checkpoint();

    #endif

    // do your memory allocations and deallocations...

    CString s = "This is a frame variable";

    // the next object is a heap object

    CPerson* p = new CPerson( "Smith", "Alan", "581-0215" );

    #ifdef _DEBUG

    newMemState.Checkpoint();

    if( diffMemState.Difference( oldMemState, newMemState ) )

    {

    TRACE( "Memory leaked!\n" );

    diffMemState.DumpStatistics();

    //or diffMemState.DumpAllObjectsSince();

    }

    #endif

    MFC在應(yīng)用程序(調(diào)試版)結(jié)束時(shí),自動(dòng)進(jìn)行內(nèi)存漏洞檢測(cè),如果存在漏洞,則輸出漏洞的有關(guān)信息。

posted @ 2006-04-17 16:09 逃逃 閱讀(674) | 評(píng)論 (2)編輯 收藏


僅列出標(biāo)題  

posts - 3, comments - 4, trackbacks - 0, articles - 0

Copyright © 逃逃

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            国产热re99久久6国产精品| 午夜一区二区三视频在线观看 | 亚洲视频电影在线| 亚洲国产日韩一区二区| 久久人人爽国产| 久久久久久久久久久久久女国产乱 | 欧美激情一区二区三区| 亚洲国产日韩综合一区| 中文欧美在线视频| 久久精品99国产精品| 久久青草福利网站| 牛人盗摄一区二区三区视频| 亚洲国产欧洲综合997久久| 日韩亚洲成人av在线| 欧美淫片网站| 欧美日韩精品综合| 精品91视频| 亚洲欧美日韩在线不卡| 欧美sm极限捆绑bd| 99xxxx成人网| 久久久青草婷婷精品综合日韩| 欧美高清一区| 国外成人在线| 在线综合欧美| 欧美11—12娇小xxxx| 一区二区电影免费观看| 久久一区二区三区四区五区| 国产精品激情| 亚洲美洲欧洲综合国产一区| 久久久夜色精品亚洲| 中日韩美女免费视频网站在线观看| 午夜久久黄色| 国产精品电影观看| 91久久精品国产91久久性色tv| 欧美一区二区三区男人的天堂| 亚洲国产精品成人精品| 午夜精彩视频在线观看不卡| 欧美精品一区二区高清在线观看| 国产日韩一区二区三区在线播放| 亚洲开发第一视频在线播放| 老司机凹凸av亚洲导航| 亚洲欧美日韩视频一区| 欧美午夜精品伦理| 日韩网站在线| 欧美电影电视剧在线观看| 欧美亚洲综合另类| 国产精品一二| 亚洲一区二区视频在线观看| 亚洲二区在线| 久久亚洲不卡| 久久一区二区三区国产精品| 一本在线高清不卡dvd| 国产欧美日韩另类一区| 一区二区三区欧美亚洲| 欧美成人免费网| 久久久久久久一区| 狠狠网亚洲精品| 久久九九全国免费精品观看| 亚洲一区二区视频在线| 国产精品你懂的在线| 亚洲欧美日韩一区在线| 99精品欧美一区二区蜜桃免费| 欧美成人中文| 亚洲精品国产精品乱码不99按摩| 男女精品网站| 欧美二区在线看| 99在线精品视频| 亚洲激情自拍| 欧美日韩网址| 欧美一区不卡| 久久成人一区| 91久久精品美女| 亚洲精品久久久久久久久久久久| 欧美日韩一区二区在线观看视频| 亚洲一区在线看| 欧美在线网址| 亚洲精品一区二| 99re热这里只有精品免费视频| 国产精品扒开腿做爽爽爽视频 | 欧美亚洲一区| 亚洲激情六月丁香| 日韩天堂在线观看| 国产精品一区久久久久| 麻豆免费精品视频| 欧美激情一区二区三区成人| 亚洲午夜精品一区二区| 欧美一级久久久久久久大片| 在线成人中文字幕| 一本色道久久88精品综合| 国产日韩欧美综合精品| 欧美国产精品v| 麻豆9191精品国产| 一区在线视频观看| 亚洲精选国产| 国产一区二区成人| 亚洲日本黄色| 韩国成人福利片在线播放| 欧美韩日一区| 国产伦精品一区| 亚洲精品久久久久久下一站 | 亚洲高清av| 一区二区三区高清视频在线观看| 韩国视频理论视频久久| 91久久精品国产91性色| 国产精品欧美日韩久久| 欧美成人精品高清在线播放| 国产精品久久久久久久免费软件| 麻豆成人综合网| 国产欧美综合在线| av不卡在线观看| 久久国产天堂福利天堂| 亚洲天天影视| 欧美黄色视屏| 免费欧美在线| 国产一级揄自揄精品视频| 日韩午夜在线视频| 亚洲国产裸拍裸体视频在线观看乱了| 日韩网站在线观看| 在线日本成人| 性欧美超级视频| 亚洲一区二区三区四区在线观看| 欧美va亚洲va国产综合| 久久婷婷综合激情| 国产精品永久入口久久久| 亚洲久色影视| 亚洲免费久久| 欧美国产日韩精品| 免费久久精品视频| 激情久久久久久久久久久久久久久久| 99成人在线| 亚洲精品欧美日韩| 欧美成人国产| 亚洲国产日韩欧美一区二区三区| 在线免费观看日本欧美| 久久久久久亚洲精品不卡4k岛国| 久久久午夜精品| 国产一区在线观看视频| 国产精品99久久久久久人| 亚洲靠逼com| 欧美久久99| 亚洲精品影院| 亚洲欧美在线网| 国产嫩草影院久久久久| 亚洲欧美在线免费观看| 香蕉久久一区二区不卡无毒影院| 国产精品高潮久久| 亚洲综合色噜噜狠狠| 性色av一区二区三区在线观看| 欧美人与性动交a欧美精品| 亚洲理伦电影| 性做久久久久久| 国产综合精品| 欧美成人小视频| a4yy欧美一区二区三区| 亚洲在线不卡| 国产一区二区成人| 欧美国产视频在线| 亚洲制服av| 欧美高清在线视频| 在线亚洲欧美专区二区| 国产精品永久免费在线| 久久综合网hezyo| 日韩亚洲一区二区| 久久国产日本精品| 亚洲精品视频中文字幕| 国产精品亚洲а∨天堂免在线| 欧美在线视频观看免费网站| 欧美国产日本韩| 99伊人成综合| 激情久久久久久久| 欧美日韩一二三区| 久久精品国产一区二区电影| 亚洲啪啪91| 久久精品视频在线看| 99国内精品| 在线观看视频一区| 国产精品卡一卡二| 欧美福利视频网站| 久久久久久免费| 午夜精品久久| 一区二区三区视频观看| 国产片一区二区| 欧美日韩国产一区二区| 欧美亚洲在线观看| 一区二区欧美视频| 久久夜色精品国产欧美乱极品| 亚洲激情在线激情| 国产麻豆精品theporn| 欧美寡妇偷汉性猛交| 久久成人在线| 亚洲欧美一区二区三区极速播放| 91久久精品一区二区三区| 久久裸体艺术| 久久成人综合网| 亚洲一区二区三区色| 一区二区激情小说| 亚洲黄网站在线观看| 一区福利视频| 国产视频一区欧美| 国产精品色一区二区三区|