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

2006年7月17日

10:04:20

論調用約定

在C語言中,假設我們有這樣的一個函數:

int function(int a,int b)

調用時只要用result = function(1,2)這樣的方式就可以使用這個函數。但是,當高級語言被編譯成計算機可以識別的機器碼時,有一個問題就凸現出來:在CPU中,計算機沒有辦法知道一個函數調用需要多少個、什么樣的參數,也沒有硬件可以保存這些參數。也就是說,計算機不知道怎么給這個函數傳遞參數,傳遞參數的工作必須由函數調用者和函數本身來協調。為此,計算機提供了一種被稱為棧的數據結構來支持參數傳遞。

棧是一種先進后出的數據結構,棧有一個存儲區、一個棧頂指針。棧頂指針指向堆棧中第一個可用的數據項(被稱為棧頂)。用戶可以在棧頂上方向棧中加入數據,這個操作被稱為壓棧(Push),壓棧以后,棧頂自動變成新加入數據項的位置,棧頂指針也隨之修改。用戶也可以從堆棧中取走棧頂,稱為彈出棧(pop),彈出棧后,棧頂下的一個元素變成棧頂,棧頂指針隨之修改。

函數調用時,調用者依次把參數壓棧,然后調用函數,函數被調用以后,在堆棧中取得數據,并進行計算。函數計算結束以后,或者調用者、或者函數本身修改堆棧,使堆?;謴驮b。

在參數傳遞中,有兩個很重要的問題必須得到明確說明:

  • 當參數個數多于一個時,按照什么順序把參數壓入堆棧
  • 函數調用后,由誰來把堆棧恢復原裝

在高級語言中,通過函數調用約定來說明這兩個問題。常見的調用約定有:

  • stdcall
  • cdecl
  • fastcall
  • thiscall
  • naked call

stdcall調用約定

stdcall很多時候被稱為pascal調用約定,因為pascal是早期很常見的一種教學用計算機程序設計語言,其語法嚴謹,使用的函數調用約定就是stdcall。在Microsoft C++系列的C/C++編譯器中,常常用PASCAL宏來聲明這個調用約定,類似的宏還有WINAPI和CALLBACK。

stdcall調用約定聲明的語法為(以前文的那個函數為例):

int __stdcall function(int a,int b)

stdcall的調用約定意味著:1)參數從右向左壓入堆棧,2)函數自身修改堆棧 3)函數名自動加前導的下劃線,后面緊跟一個@符號,其后緊跟著參數的尺寸

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

push 2 第二個參數入棧 push 1 第一個參數入棧 call function 調用參數,注意此時自動把cs:eip入棧

而對于函數自身,則可以翻譯為:

push ebp 保存ebp寄存器,該寄存器將用來保存堆棧的棧頂指針,可以在函數退出時恢復 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 恢復esp pop ebp ret 8

而在編譯時,這個函數的名字被翻譯成_function@8

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

從函數調用看,2和1依次被push進堆棧,而在函數中又通過相對于ebp(即剛進函數時的堆棧指針)的偏移量存取參數。函數結束后,ret 8表示清理8個字節的堆棧,函數自己恢復了堆棧。

cdecl調用約定

cdecl調用約定又稱為C調用約定,是C語言缺省的調用約定,它的定義語法是:

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

在寫本文時,出乎我的意料,發現cdecl調用約定的參數壓棧順序是和stdcall是一樣的,參數首先由有向左壓入堆棧。所不同的是,函數本身不清理堆棧,調用者負責清理堆棧。由于這種變化,C調用約定允許函數的參數的個數是不固定的,這也是C語言的一大特色。對于前面的function函數,使用cdecl后的匯編碼變成:

調用處 push 1 push 2 call function add esp,8 注意:這里調用者在恢復堆棧被調用函數_function處 push ebp 保存ebp寄存器,該寄存器將用來保存堆棧的棧頂指針,可以在函數退出時恢復 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 恢復esp pop ebp ret 注意,這里沒有修改堆棧

MSDN中說,該修飾自動在函數名前加前導的下劃線,因此函數名在符號表中被記錄為_function,但是我在編譯時似乎沒有看到這種變化。

由于參數按照從右向左順序壓棧,因此最開始的參數在最接近棧頂的位置,因此當采用不定個數參數時,第一個參數在棧中的位置肯定能知道,只要不定的參數個數能夠根據第一個后者后續的明確的參數確定下來,就可以使用不定參數,例如對于CRT中的sprintf函數,定義為:

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

由于所有的不定參數都可以通過format確定,因此使用不定個數的參數是沒有問題的。

fastcall

fastcall調用約定和stdcall類似,它意味著:

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

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

thiscall

thiscall是唯一一個不能明確指明的函數修飾,因為thiscall不是關鍵字。它是C++類成員函數缺省的調用約定。由于成員函數調用還有一個this指針,因此必須特殊處理,thiscall意味著:

  • 參數從右向左入棧
  • 如果參數個數確定,this指針通過ecx傳遞給被調用者;如果參數個數不確定,this指針在所有參數壓棧后被壓入堆棧。
  • 對參數個數不定的,調用者清理堆棧,否則函數自己清理堆棧

為了說明這個調用約定,定義如下類和使用代碼:

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函數被翻譯成匯編后就變成:

//函數function1調用 0401C1D push 2 00401C1F push 1 00401C21 lea ecx,[ebp-8] 00401C24 call function1 注意,這里this沒有被入棧 //函數function2調用 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

可見,對于參數個數固定情況下,它類似于stdcall,不定時則類似cdecl

naked call

這是一個很少見的調用約定,一般程序設計者建議不要使用。編譯器不會給這種函數增加初始化和清理代碼,更特殊的是,你不能用return返回返回值,只能用插入匯編返回結果。這一般用于實模式驅動程序設計,假設定義一個求和的加法程序,可以定義為:

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

注意,這個函數沒有顯式的return返回值,返回通過修改eax寄存器實現,而且連退出函數的ret指令都必須顯式插入。上面代碼被翻譯成匯編以后變成:

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

注意這個修飾是和__stdcall及cdecl結合使用的,前面是它和cdecl結合使用的代碼,對于和stdcall結合的代碼,則變成:

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

至于這種函數被調用,則和普通的cdecl及stdcall調用函數一致。

函數調用約定導致的常見問題

如果定義的約定和使用的約定不一致,則將導致堆棧被破壞,導致嚴重問題,下面是兩種常見的問題:

  1. 函數原型聲明和函數體定義不一致
  2. DLL導入函數時聲明了不同的函數約定

以后者為例,假設我們在dll種聲明了一種函數為:

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

使用時代碼為:

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

由于調用者沒有理解WINAPI的含義錯誤的增加了這個修飾,上述代碼必然導致堆棧被破壞,MFC在編譯時插入的checkesp函數將告訴你,堆棧被破壞了。

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


2006年5月22日

1、文件操作的方法

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

(1)使用標準C運行庫函數,包括fopen、fclose、fseek等。

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

???(3)使用Win32下的文件和目錄操作函數,如CreateFile,CopyFile,DeleteFile,FindNextFile,等等。

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

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


2、文件操作的方法
MFC用一些類來封裝文件訪問的Win32 API。以CFile為基礎,從CFile派生出幾個類,如CStdioFile,CMemFile,MFC內部使用的CMiororFile,等等。

2.1、CFile的結構

?????2.1.1、CFile定義的枚舉類型

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

  1. OpenFlags

    OpenFlags定義了13種文件訪問和共享模式:

    enum OpenFlags {

    //第一(從右,下同)至第二位,打開文件時訪問模式,讀/寫/讀寫

    modeRead = 0x0000,

    modeWrite = 0x0001,

    modeReadWrite = 0x0002,

    shareCompat = 0x0000, //32位MFC中沒用

    //第五到第七位,打開文件時的共享模式

    shareExclusive = 0x0010,//獨占方式,禁止其他進程讀寫

    shareDenyWrite = 0x0020,//禁止其他進程寫

    shareDenyRead = 0x0030,//禁止其他進程讀

    shareDenyNone = 0x0040,//允許其他進程寫

    //第八位,打開文件時的文件繼承方式

    modeNoInherit = 0x0080,//不允許子進程繼承

    //第十三、十四位,是否創建新文件和創建方式

    modeCreate = 0x1000,//創建新文件,文件長度0

    modeNoTruncate = 0x2000,//創建新文件時如文件已存在則打開

    //第十五、十六位,文件以二進制或者文本方式打開,在派生類CStdioFile中用

    typeText = 0x4000,

    typeBinary = (int)0x8000

    };

  2. Attribute

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

    enum Attribute {

    normal = 0x00,

    readOnly = 0x01,

    hidden = 0x02,

    system = 0x04,

    volume = 0x08,

    directory = 0x10,

    archive = 0x20

    }

  3. SeekPosition

    SeekPosition定義了三種文件位置:頭、尾、當前:

    enum SeekPosition{

    begin = 0x0,

    current = 0x1,

    end = 0x2

    };

  4. hFileNull

hFileNull定義了空文件句柄

enum { hFileNull = -1 };

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

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

UINT m_hFile

該成員變量是public訪問屬性,保存::CreateFile返回的操作系統的文件句柄。MFC重載了運算符號HFILE來返回m_hFile,這樣在使用HFILE類型變量的地方可以使用CFile對象。

BOOL m_bCloseOnDelete;

CString m_strFileName;

這兩個成員變量是protected訪問屬性。m_bCloseOnDelete用來指示是否在關閉文件時刪除CFile對象;m_strFileName用來保存文件名。

2.1.3、CFile的成員函數

CFile的成員函數實現了對Win32文件操作函數的封裝,完成以下動作:打開、創建、關閉文件,文件指針定位,文件的鎖定與解鎖,文件狀態的讀取和修改,等等。其中,用到了m_hFile文件句柄的一般是虛擬函數,和此無關的一般是靜態成員函數。一般地,成員函數被映射到對應的Win32函數,如表11-1所示。

表11-1 CFile函數對Win32文件函數的封裝

虛擬

靜態

成員函數

對應的Win32函數

文件的創建、打開、關閉

 

Abort

CloseHandle

 

Duplicate

DuplicateHandle

 

Open

CreateFile

 

Close

CloseHandle

文件的讀寫

 

Read

ReadFile

   

ReadHuge(向后兼容)

調用Read成員函數

 

Write

WriteFile

   

WriteHuage(向后兼容)

調用Write成員函數

 

Flush

FlushFileBuffers

文件定位

 

Seek

SetFilePointer

   

SeekToBegin

調用Seek成員函數

   

SeekToEnd

調用Seek成員函數

 

GetLength

調用Seek成員函數

 

SetLength

SetEndOfFile

文件的鎖定/解鎖

 

LockRange

LockFile

 

UnlockRange

UnlockFile

文件狀態操作函數

 

GetPosition

SetFilePointer

   

GetStatus(CFileStatus&)

GetFileTime,GetFileSize等

 

GetStatus(LPSTR lpszFileName CFileStatus&)

FindFirstFile

 

GetFileName

不是簡單地映射到某個函數

 

GetFileTitle

 

 

GetFilePath

 

 

SetFilePath

 
 

SetStatus

 

改名和刪除

 

Rename

MoveFile

 

Remove

DeleteFile


2.1.4、CFile的部分實現


這里主要討論CFile對象的構造函數和文件的打開/創建的過程。

  1. 構造函數

CFile有如下幾個構造函數:

  • CFile()

缺省構造函數,僅僅構造一個CFile對象,還必須使用Open成員函數來打開文件。

  • CFile(int hFile)

已經打開了一個文件hFile,在此基礎上構造一個CFile對象來給它打包。HFile將被賦值給CFile的成員變量m_hFile。

  • CFile(LPCTSTR lpszFileName, UINT nOpenFlags)

指定一個文件名和文件打開方式,構造CFile對象,調用Open打開/創建文件,把文件句柄保存到m_hFile。

  1. 打開/創建文件

Open的原型如下:

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

CFileException* pException)

Open調用Win32函數::CreateFile打開文件,并把文件句柄保存到成員變量m_hFile中。

CreateFile函數的原型如下:

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必須把自己的兩個參數lpszFileName和nOpenFlags映射到CreateFile的七個參數上。

從OpenFlags的定義可以看出,(nOpenFlags & 3)表示了讀寫標識,映射成變量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定義了一個局部的SECURITY_ATTRIBUTES變量sa,(nOpenFlags & 0x80)被賦值給sa.bInheritHandle。

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

在生成了上述參數之后,先調用::CreateFile:

HANDLE hFile =::CreateFile(lpszFileName,

dwAccess, dwShareMode, &sa,

dwCreateFlag, FILE_ATTRIBUTE_NORMAL, NULL);

然后,hFile被賦值給成員變量m_hFile,m_bCloseOnDelete被設置為TRUE。

由上可以看出,CFile打開(創建)一個文件時大大簡化了:: CreateFile函數的復雜性,即只需要指定一個文件名、一個打開文件的參數即可。若該參數指定為0,則表示以只讀方式打開一個存在的文件,獨占使用,不允許子進程繼承。

在CFile對象使用時,如果它是在堆中分配的,則應該銷毀它;如果在棧中分配的,則CFile對象將被自動銷毀。銷毀時析構函數被調用,析構函數是虛擬函數。若m_bCloseOnDelete為真且m_hFile非空,則析構函數調用Close關閉文件。

至于其他CFile成員函數的實現,這里不作分析了。

2.1.5、CFile的派生類

這里主要簡要地介紹CStdioFile和CmemFile及CFileFind。

  1. CStdioFile

    CStdioFile對文本文件進行操作。

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

    另外,CStdioFile不支持CFile的Dumplicate、LockRange、UnlockRange操作,但是實現了兩個新的操作ReadString和WriteString。

  2. CMemFile

    CMemFile把一塊內存當作一個文件來操作,所以,它沒有打開文件的操作,而是設計了Attach和Detach用來分配或者釋放一塊內存。相應地,它提供了Alloc、Free虛擬函數來操作內存文件,它覆蓋了Read、Write來讀寫內存文件。

  3. CFileFind

為了方便文件查找,MFC把有關功能歸結成為一個類CFileFind。CFileFind派生于CObject類。首先,它使用FindFile和FineNextFile包裝了Win32函數::FindFirstFile和::FindNextFile;其次,它提供了許多函數用來獲取文件的狀態或者屬性。

使用CFileStatus結構來描述文件的屬性,其定義如下:

struct CFileStatus

{

CTime m_ctime; // 文件創建時間

CTime m_mtime; // 文件最近一次修改時間

CTime m_atime; // 文件最近一次訪問時間

LONG m_size; // 文件大小

BYTE m_attribute; // 文件屬性

BYTE _m_padding; // 沒有實際含義,用來增加一個字節

TCHAR m_szFullName[_MAX_PATH]; //絕對路徑

#ifdef _DEBUG

//實現Dump虛擬函數,輸出文件屬性

void Dump(CDumpContext& dc) const;

#endif

};

例如:

CFileStatus status;

pFile->GetStatus(status);

#ifdef _DEBUG

status.dump(afxDump);

#endif

 

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


2006年4月17日

10.1內存分配?
??10.1.1?內存分配函數

MFCWin32或者C語言的內存分配API,有四種內存分配API可供使用。

  1. Win32的堆分配函數

    每一個進程都可以使用堆分配函數創建一個私有的堆──調用進程地址空間的一個或者多個頁面。DLL創建的私有堆必定在調用DLL的進程的地址空間內,只能被調用進程訪問。

    HeapCreate用來創建堆;HeapAlloc用來從堆中分配一定數量的空間,HeapAlloc分配的內存是不能移動的;HeapSize可以確定從堆中分配的空間的大小;HeapFree用來釋放從堆中分配的空間;HeapDestroy銷毀創建的堆。

  2. Windows傳統的全局或者局部內存分配函數

    由于Win32采用平面內存結構模式,Win32下的全局和局部內存函數除了名字不同外,其他完全相同。任一函數都可以用來分配任意大小的內存(僅僅受可用物理內存的限制)。用法可以和Win16下基本一樣。

    Win32下保留這類函數保證了和Win16的兼容。

  3. C語言的標準內存分配函數

    C語言的標準內存分配函數包括以下函數:

    malloc,calloc,realloc,free,等。

    這些函數最后都映射成堆API函數,所以,malloc分配的內存是不能移動的。這些函數的調式版本為

    malloc_dbg,calloc_dbg,realloc_dbg,free_dbg,等。

  4. Win32的虛擬內存分配函數

虛擬內存API是其他API的基礎。虛擬內存API以頁為最小分配單位,X86上頁長度為4KB,可以用GetSystemInfo函數提取頁長度。虛擬內存分配函數包括以下函數:

  • LPVOID VirtualAlloc(LPVOID lpvAddress,

DWORD cbSize,

DWORD fdwAllocationType,

DWORD fdwProtect);

該函數用來分配一定范圍的虛擬頁。參數1指定起始地址;參數2指定分配內存的長度;參數3指定分配方式,取值MEM_COMMINT或者MEM_RESERVE;參數4指定控制訪問本次分配的內存的標識,取值為PAGE_READONLY、PAGE_READWRITE或者PAGE_NOACCESS。

  • LPVOID VirtualAllocEx(HANDLE process,

LPVOID lpvAddress,

DWORD cbSize,

DWORD fdwAllocationType,

DWORD fdwProtect);

該函數功能類似于VirtualAlloc,但是允許指定進程process。VirtaulFree、VirtualProtect、VirtualQuery都有對應的擴展函數。

  • BOOL VirtualFree(LPVOID lpvAddress,

DWORD dwSize,

DWORD dwFreeType);

該函數用來回收或者釋放分配的虛擬內存。參數1指定希望回收或者釋放內存的基地址;如果是回收,參數2可以指向虛擬地址范圍內的任何地方,如果是釋放,參數2必須是VirtualAlloc返回的地址;參數3指定是否釋放或者回收內存,取值為MEM_DECOMMINT或者MEM_RELEASE。

  • BOOL VirtualProtect(LPVOID lpvAddress,

DWORD cbSize,

DWORD fdwNewProtect,

PDWORD pfdwOldProtect);

該函數用來把已經分配的頁改變成保護頁。參數1指定分配頁的基地址;參數2指定保護頁的長度;參數3指定頁的保護屬性,取值PAGE_READ、PAGE_WRITE、PAGE_READWRITE等等;參數4用來返回原來的保護屬性。

  • DWORD VirtualQuery(LPCVOID lpAddress,

PMEMORY_BASIC_INFORMATION lpBuffer,

DWORD dwLength

);

該函數用來查詢內存中指定頁的特性。參數1指向希望查詢的虛擬地址;參數2是指向內存基本信息結構的指針;參數3指定查詢的長度。

  • BOOL VirtualLock(LPVOID lpAddress,DWORD dwSize);

該函數用來鎖定內存,鎖定的內存頁不能交換到頁文件。參數1指定要鎖定內存的起始地址;參數2指定鎖定的長度。

  • BOOL VirtualUnLock(LPVOID lpAddress,DWORD dwSize);

參數1指定要解鎖的內存的起始地址;參數2指定要解鎖的內存的長度。


C++的new 和 delete操作符

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

  1. 全局范圍內的new和delete操作符

    原型如下:

    void _cdecl ::operator new(size_t nSize);

    void __cdecl operator delete(void* p);

    調試版本:

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

    LPCSTR lpszFileName, int nLine)

  2. 類定義的new和delete操作符

原型如下:

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

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

類的operator new操作符是類的靜態成員函數,對該類的對象來說將覆蓋全局的operator new。全局的operator new用來給內部類型對象(如int)、沒有定義operator new操作符的類的對象分配內存。

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



10.1.2調試手段

MFC應用程序可以使用C運行庫的調試手段,也可以使用MFC提供的調試手段。兩種調試手段分別論述如下。

  1. C運行庫提供和支持的調試功能

    C運行庫提供和支持的調試功能如下:

    1. 調試信息報告函數

      用來報告應用程序的調試版本運行時的警告和出錯信息。包括:

      _CrtDbgReport 用來報告調試信息;

      _CrtSetReportMode 設置是否警告、出錯或者斷言信息;

      _CrtSetReportFile 設置是否把調試信息寫入到一個文件。

    2. 條件驗證或者斷言宏:

      斷言宏主要有:

      assert 檢驗某個條件是否滿足,不滿足終止程序執行。

      驗證函數主要有:

      _CrtIsValidHeapPointer 驗證某個指針是否在本地堆中;

      _CrtIsValidPointer 驗證指定范圍的內存是否可以讀寫;

      _CrtIsMemoryBlock 驗證某個內存塊是否在本地堆中。

    3. 內存(堆)調試:

    malloc_dbg 分配內存時保存有關內存分配的信息,如在什么文件、哪一行分配的內存等。有一系列用來提供內存診斷的函數:

    _CrtMemCheckpoint 保存內存快照在一個_CrtMemState結構中;

    _CrtMemDifference 比較兩個_CrtMemState;

    _CrtMemDumpStatistics 轉儲輸出一_CrtMemState結構的內容;

    _CrtMemDumpAllObjectsSince 輸出上次快照或程序開始執行以來在堆中分配的所有對象的信息;

    _CrtDumpMemoryLeaks 檢測程序執行以來的內存漏洞,如果有漏洞則輸出所有分配的對象。

    2.????? MFC提供的調試手段

    MFC在C運行庫提供和支持的調試功能基礎上,設計了一些類、函數等來協助調試。

    1. MFC的TRACE、ASSERT

      ASSERT

      使用ASSERT斷言判定程序是否可以繼續執行。

      TRACE

      使用TRACE宏顯示或者打印調試信息。TRACE是通過函數AfxTrace實現的。由于AfxTrace函數使用了cdecl調用約定,故可以接受個數不定的參數,如同printf函數一樣。它的定義和實現如下:

      void AFX_CDECL AfxTrace(LPCTSTR lpszFormat, ...)

      {

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

      if (!afxTraceEnabled)

      return;

      #endif

      //處理個數不定的參數

      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為TRUE,則TRACE宏可以輸出;否則,沒有TRACE信息被輸出。如果通過afxTraceFlags指定了跟蹤什么消息,則輸出有關跟蹤信息,例如為了指定“Multilple Application Debug”,令AfxTraceFlags|=traceMultiApp??梢愿櫟男畔⒂校?/p>

      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

      };

      這樣,應用程序可以在需要的地方指定afxTraceEnabled的值打開或者關閉TRACE開關,指定AfxTraceFlags的值過濾跟蹤信息。

      Visual C++提供了一個TRACE工具,也可以用來完成上述功能。

      為了顯示消息信息,MFC內部定義了一個AFX_MAP_MESSAG類型的數組allMessages,儲存了Windows消息和消息名映射對。例如:

      allMessages[1].nMsg = WM_CREATE,

      allMessages[1].lpszMsg = “WM_CREATE”

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

      allMessages和函數_AfxTraceMsg的詳細實現可以參見AfxTrace.cpp。

    2. MFC對象內容轉儲

      對象內容轉儲是CObject類提供的功能,所有從它派生的類都可以通過覆蓋虛擬函數DUMP來支持該功能。在講述CObject類時曾提到過。

      虛擬函數Dump的定義:

      class ClassName : public CObject

      {

      public:

      #ifdef _DEBUG

      virtual void Dump( CDumpContext& dc ) const;

      #endif

      };

      在使用Dump時,必須給它提供一個CDumpContext類型的參數,該參數指定的對象將負責輸出調試信息。為此,MFC提供了一個預定義的全局CDumpContext對象afxDump,它把調試信息輸送給調試器的調試窗口。從前面AfxTrace的實現可以知道,MFC使用了afxDump輸出跟蹤信息到調試窗口。

      CDumpContext類沒有基類,它提供了以文本形式輸出診斷信息的功能。

      例如:

      CPerson* pMyPerson = new CPerson;

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

      //...

      // now dump the contents

      #ifdef _DEBUG

      pMyPerson->Dump( afxDump );

      #endif

    3. MFC對象有效性檢測

    對象有效性檢測是CObject類提供的功能,所有從它派生的類都可以通過覆蓋虛擬函數AssertValid來支持該功能。在講述CObject類時曾提到過。

    虛擬函數AssertValid的定義:

    class ClassName : public CObject

    {

    public:

    #ifdef _DEBUG

    virtual void AssertValid( ) const;

    #endif

    };

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

    另外,MFC提供了一些函數來判斷地址是否有效,如:

    AfxIsMemoryBlock,AfxIsString,AfxIsValidAddress。

    10.1.3內存診斷

    MFC使用DEBUG_NEW來跟蹤內存分配時的執行的源碼文件和行數。

    把#define new DEBUG_NEW插入到每一個源文件中,這樣,調試版本就使用_malloc_dbg來分配內存。MFC Appwizard在創建框架文件時已經作了這樣的處理。

    1. AfxDoForAllObjects

      MFC提供了函數AfxDoForAllObjects來追蹤動態分配的內存對象,函數原型如下:

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

      void* pContext), void* pContext );

      其中:

      參數1是一個函數指針,AfxDoForAllObjects對每個對象調用該指針表示的函數。

      參數2將傳遞給參數1指定的函數。

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

    10.1.4內存漏洞檢測

    僅僅用于new的DEBUG版本分配的內存。

    完成內存漏洞檢測,需要如下系列步驟:

    • 調用AfxEnableMemoryTracking(TRUE/FALSE)打開/關閉內存診斷。在調試版本下,缺省是打開的;關閉內存診斷可以加快程序執行速度,減少診斷輸出。

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

    afxMemDF,delayFreeMemDF,checkAlwaysMemDF

    其中:allocMemDF表示可以進行內存診斷輸出;delayFreeMemDF表示是否是在應用程序結束時才調用free或者delete,這樣導致程序最大可能的分配內存;checkAlwaysMemDF表示每一次分配或者釋放內存之后都調用函數AfxCheckMemory進行內存檢測(AfxCheckMemory檢查堆中所有通過new分配的內存(不含malloc))。

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

    • 創建一個CMemState類型的變量oldMemState,調用CMemState的成員函數CheckPoint獲得初次內存快照。

    • 執行了系列內存分配或者釋放之后,創建另一個CMemState類型變量newMemState,調用CMemState的成員函數CheckPoint獲得新的內存快照。

    • 創建第三個CMemState類型變量difMemState,調用CMemState的成員函數Difference比較oldMemState和newMemState,結果保存在變量difMemState中。如果沒有不同,則返回FALSE,否則返回TRUE。

    • 如果不同,則調用成員函數DumpStatistics輸出比較結果。

    例如:

    // 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在應用程序(調試版)結束時,自動進行內存漏洞檢測,如果存在漏洞,則輸出漏洞的有關信息。

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


僅列出標題  

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>
            亚洲免费激情| 欧美理论视频| 亚洲欧洲日产国产网站| 久久在线免费观看| 久久激情五月丁香伊人| 久久99伊人| 欧美日韩一二三区| 国产精品免费一区二区三区观看| 国产精品卡一卡二卡三| 国产日韩欧美精品| 亚洲精品免费在线| 亚洲伊人网站| 久久一二三国产| 亚洲美女网站| 久久精品国产一区二区电影| 欧美成人午夜免费视在线看片| 欧美激情亚洲一区| 一区二区三区欧美在线| 亚洲免费人成在线视频观看| 亚洲国产精品专区久久 | 欧美va天堂| 99综合在线| 欧美国产日产韩国视频| 宅男噜噜噜66一区二区| 午夜欧美不卡精品aaaaa| 欧美综合国产| 91久久线看在观草草青青| 亚洲四色影视在线观看| 老色鬼精品视频在线观看播放| 欧美三级日本三级少妇99| 国产一区免费视频| 一片黄亚洲嫩模| 欧美成人精品在线播放| 亚洲无人区一区| 欧美人成免费网站| 亚洲电影免费观看高清| 久久精品夜色噜噜亚洲aⅴ| 亚洲精品日韩久久| 噜噜噜躁狠狠躁狠狠精品视频| 国产精品麻豆成人av电影艾秋 | 国产自产在线视频一区| 在线天堂一区av电影| 欧美福利网址| 久久久一区二区| 国产亚洲精品久久久久婷婷瑜伽| 一区二区欧美亚洲| 亚洲国产成人在线播放| 久久久久久电影| 国产亚洲成人一区| 午夜久久影院| 亚洲一区亚洲二区| 国产精品久久777777毛茸茸| 99re热这里只有精品视频| 欧美高清视频一区| 浪潮色综合久久天堂| 在线精品一区| 美女黄毛**国产精品啪啪| 欧美一区二区三区四区在线| 国产精品国产三级国产aⅴ9色| 亚洲乱码视频| 亚洲精品国产精品国自产在线 | 欧美高清在线播放| 亚洲一区二区视频在线| 美国十次成人| 性高湖久久久久久久久| 国产精品视频一二三| 午夜视频在线观看一区二区三区 | 国产精品一区二区三区成人| 亚洲四色影视在线观看| 亚洲精品免费电影| 欧美三级不卡| 性欧美xxxx大乳国产app| 亚洲一区二区三区国产| 国产精品一区二区女厕厕| 欧美专区在线播放| 欧美一站二站| 亚洲激情社区| 一本大道av伊人久久综合| 国产精品国产三级国产普通话99| 午夜精品久久久久久久白皮肤 | 亚洲精品一区二区三区蜜桃久| 欧美风情在线观看| 欧美日韩不卡合集视频| 亚洲欧美日韩精品| 久久精品国产精品亚洲综合| 亚洲国产精品一区二区第一页| 亚洲激情视频在线观看| 国产精品久久国产愉拍| 久久嫩草精品久久久久| 欧美激情一级片一区二区| 亚洲女人天堂av| 久久婷婷麻豆| 亚洲永久免费观看| 久久天天躁狠狠躁夜夜av| 中文久久乱码一区二区| 久久国产精品电影| 亚洲视频欧美在线| 久久久久9999亚洲精品| 亚洲砖区区免费| 蜜桃久久av一区| 久久国产主播| 欧美日韩国语| 欧美成人资源| 国产一区二区成人| 一本综合久久| 91久久久亚洲精品| 午夜久久tv| 亚洲欧美日韩成人| 欧美福利影院| 欧美成人激情在线| 国产一区二区丝袜高跟鞋图片 | 蜜桃精品一区二区三区| 欧美资源在线观看| 欧美视频在线观看视频极品| 欧美国产大片| 极品尤物久久久av免费看| 亚洲欧美日韩国产一区二区| 亚洲午夜三级在线| 欧美精品一级| 久久综合伊人77777尤物| 亚洲精品久久视频| 国产欧美精品在线观看| 在线亚洲免费| 欧美一区二区三区视频在线| 国产在线播放一区二区三区| 久久久蜜桃一区二区人| 欧美国产精品v| 欧美一级久久久| 亚洲一区二区三区乱码aⅴ蜜桃女| 欧美1区2区| 美女视频黄 久久| 国产精品毛片在线看| 亚洲精品久久久蜜桃| 亚洲精品自在久久| 欧美激情影音先锋| 亚洲国产精品精华液网站| 亚洲第一区中文99精品| 久久av在线| 久久亚洲综合色| 韩国免费一区| 麻豆精品传媒视频| 欧美国产1区2区| 亚洲乱码一区二区| 国产精品v亚洲精品v日韩精品 | 99成人在线| 在线视频欧美日韩精品| 欧美日本久久| 亚洲在线观看免费视频| 欧美一二三区在线观看| 国产精品一区在线播放| 午夜免费日韩视频| 免费高清在线一区| 亚洲高清久久久| 欧美精品一区二区三区蜜桃 | 在线午夜精品自拍| 欧美一区1区三区3区公司| 国产在线成人| 另类国产ts人妖高潮视频| 亚洲人成小说网站色在线| 一区二区精品| 国产一区二区三区日韩| 久久一二三国产| 亚洲精选一区| 欧美在线观看视频一区二区| 国产综合精品一区| 欧美激情精品久久久| 亚洲综合精品| 免费久久99精品国产| 99视频+国产日韩欧美| 国产精品午夜视频| 久久综合久久综合九色| 91久久视频| 久久久久九九九| 99成人精品| 香蕉av福利精品导航| 久久国产精品第一页| 欧美日韩一区二区三区在线看 | 欧美激情国产日韩精品一区18| 亚洲精品一区在线| 国产精品亚洲成人| 欧美国产视频一区二区| 香蕉免费一区二区三区在线观看 | 日韩亚洲欧美成人| 国产一区二区三区在线观看免费视频| 欧美成人自拍视频| 欧美一区二区三区精品电影| 亚洲欧洲一区二区三区在线观看 | 亚洲愉拍自拍另类高清精品| 亚洲电影免费在线观看| 国产乱人伦精品一区二区 | 久久蜜臀精品av| 一区二区三区.www| 亚洲成人直播| 国产日韩欧美在线一区| 欧美日韩小视频| 欧美成人一区二免费视频软件| 欧美一区二区三区在线看| 国内精品美女在线观看| 欧美日韩一区三区四区| 欧美成人国产一区二区|