??xml version="1.0" encoding="utf-8" standalone="yes"?>国产精品久久久久蜜芽,…久久精品99久久香蕉国产,日日狠狠久久偷偷色综合0http://www.shnenglu.com/RancyGe/zh-cnThu, 08 May 2025 19:51:12 GMTThu, 08 May 2025 19:51:12 GMT60用约?http://www.shnenglu.com/RancyGe/archive/2006/07/17/10150.html逃?/dc:creator>逃?/author>Mon, 17 Jul 2006 02:06:00 GMThttp://www.shnenglu.com/RancyGe/archive/2006/07/17/10150.htmlhttp://www.shnenglu.com/RancyGe/comments/10150.htmlhttp://www.shnenglu.com/RancyGe/archive/2006/07/17/10150.html#Feedback1http://www.shnenglu.com/RancyGe/comments/commentRss/10150.htmlhttp://www.shnenglu.com/RancyGe/services/trackbacks/10150.html

用约?

在C语言中,假设我们有这L一个函敎ͼ

int function(int a,int b)

调用时只要用result = function(1,2)q样的方式就可以使用q个函数。但是,当高U语a被编译成计算机可以识别的机器码时Q有一个问题就凸现出来Q在CPU中,计算机没有办法知道一个函数调用需要多个、什么样的参敎ͼ也没有硬件可以保存这些参数。也是_计算Z知道怎么l这个函C递参敎ͼ传递参数的工作必须由函数调用者和函数本n来协调。ؓ此,计算机提供了一U被UCؓ栈的数据l构来支持参C递?/p>

栈是一U先q后出的数据l构Q栈有一个存储区、一个栈指针。栈指针指向堆栈中W一个可用的数据(被称为栈Ӟ。用户可以在栈顶上方向栈中加入数据,q个操作被称为压?Push)Q压栈以后,栈顶自动变成新加入数据项的位|,栈顶指针也随之修攏V用户也可以从堆栈中取走栈顶Q称为弹出栈(pop)Q弹出栈后,栈顶下的一个元素变成栈Ӟ栈顶指针随之修改?/p>

函数调用Ӟ调用者依ơ把参数压栈Q然后调用函敎ͼ函数被调用以后,在堆栈中取得数据Qƈq行计算。函数计结束以后,或者调用者、或者函数本w修改堆栈,使堆栈恢复原装?/p>

在参C递中Q有两个很重要的问题必须得到明确说明Q?/p>

  • 当参C数多于一个时Q按照什么顺序把参数压入堆栈
  • 函数调用后,p来把堆栈恢复原装

在高U语a中,通过函数调用U定来说明这两个问题。常见的调用U定有:

  • stdcall
  • cdecl
  • fastcall
  • thiscall
  • naked call

stdcall调用U定

stdcall很多时候被UCؓpascal调用U定Q因为pascal是早期很常见的一U教学用计算机程序设计语aQ其语法严}Q用的函数调用U定是stdcall。在Microsoft C++pd的C/C++~译器中Q常常用PASCAL宏来声明q个调用U定Q类似的宏还有WINAPI和CALLBACK?/p>

stdcall调用U定声明的语法ؓ(以前文的那个函数ZQ:

int __stdcall function(int a,int b)

stdcall的调用约定意味着Q?Q参C叛_左压入堆栈,2Q函数自w修改堆?3)函数名自动加前导的下划线Q后面紧跟一个@W号Q其后紧跟着参数的尺?/p>

以上q这个函Cؓ例,参数b首先被压栈,然后是参数aQ函数调用function(1,2)调用处翻译成汇编语言变成:

push 2 W二个参数入?push 1 W一个参数入?call function 调用参数Q注意此时自动把cs:eip入栈

而对于函数自w,则可以翻译ؓQ?

push ebp 保存ebp寄存器,该寄存器用来保存堆栈的栈顶指针Q可以在函数退出时恢复 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

而在~译Ӟq个函数的名字被译成_function@8

注意不同~译器会插入自己的汇~代码以提供~译的通用性,但是大体代码如此。其中在函数开始处保留esp到ebp中,在函数结束恢复是~译器常用的Ҏ?/p>

从函数调用看Q??依次被pushq堆栈,而在函数中又通过相对于ebp(卛_q函数时的堆栈指针)的偏U量存取参数。函数结束后Qret 8表示清理8个字节的堆栈Q函数自己恢复了堆栈?/p>

cdecl调用U定

cdecl调用U定又称为C调用U定Q是C语言~省的调用约定,它的定义语法是:

int function (int a ,int b) //不加修饰是C调用U定 int __cdecl function(int a,int b)//明确指出C调用U定

在写本文ӞZ我的意料Q发现cdecl调用U定的参数压栈顺序是和stdcall是一LQ参数首先由有向左压入堆栈。所不同的是Q函数本w不清理堆栈Q调用者负责清理堆栈。由于这U变化,C调用U定允许函数的参数的个数是不固定的,q也是C语言的一大特艌Ӏ对于前面的function函数Q用cdecl后的汇编码变成:

调用?/b> push 1 push 2 call function add esp,8 注意Q这里调用者在恢复堆栈被调用函数_function?/b> push ebp 保存ebp寄存器,该寄存器用来保存堆栈的栈顶指针Q可以在函数退出时恢复 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 注意Q这里没有修改堆?/b>

MSDN中说Q该修饰自动在函数名前加前导的下划线Q因此函数名在符可中被记录为_functionQ但是我在编译时g没有看到q种变化?/p>

׃参数按照从右向左序压栈Q因此最开始的参数在最接近栈顶的位|,因此当采用不定个数参数时Q第一个参数在栈中的位|肯定能知道Q只要不定的参数个数能够ҎW一个后者后l的明确的参数确定下来,可以用不定参敎ͼ例如对于CRT中的sprintf函数Q定义ؓQ?

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

׃所有的不定参数都可以通过format定Q因此用不定个数的参数是没有问题的?/p>

fastcall

fastcall调用U定和stdcallcMQ它意味着Q?

  • 函数的第一个和W二个DWORD参数Q或者尺寸更的Q通过ecx和edx传递,其他参数通过从右向左的顺序压?
  • 被调用函数清理堆?
  • 函数名修改规则同stdcall

其声明语法ؓQint fastcall function(int a,int b)

thiscall

thiscall是唯一一个不能明指明的函数修饰Q因为thiscall不是关键字。它是C++cL员函数缺省的调用U定。由于成员函数调用还有一个this指针Q因此必ȝD处理,thiscall意味着Q?

  • 参数从右向左入栈
  • 如果参数个数定Qthis指针通过ecx传递给被调用者;如果参数个数不确定,this指针在所有参数压栈后被压入堆栈?
  • 对参CC定的Q调用者清理堆栈,否则函数自己清理堆栈

Z说明q个调用U定Q定义如下类和用代码:

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函数被翻译成汇编后就变成Q?

//函数function1调用 0401C1D push 2 00401C1F push 1 00401C21 lea ecx,[ebp-8] 00401C24 call function1 注意Q这里this没有被入?//函数function2调用 00401C29 push 3 00401C2B push 2 00401C2D push 1 00401C2F push 3 00401C31 lea eax,[ebp-8] q里引入this指针 00401C34 push eax 00401C35 call function2 00401C3A add esp,14h

可见Q对于参C数固定情况下Q它cM于stdcallQ不定时则类似cdecl

naked call

q是一个很见的调用约定,一般程序设计者徏议不要用。编译器不会l这U函数增加初始化和清理代码,更特D的是,你不能用returnq回q回|只能用插入汇~返回结果。这一般用于实模式驱动E序设计Q假讑֮义一个求和的加法E序Q可以定义ؓQ?

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

注意Q这个函数没有显式的returnq回|q回通过修改eax寄存器实玎ͼ而且q退出函数的ret指o都必L式插入。上面代码被译成汇~以后变成:

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

注意q个修饰是和__stdcall及cdecll合使用的,前面是它和cdecll合使用的代码,对于和stdcalll合的代码,则变成:

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

至于q种函数被调用,则和普通的cdecl及stdcall调用函数一致?/p>

函数调用U定D的常见问?/h2>

如果定义的约定和使用的约定不一_则将D堆栈被破坏,D严重问题Q下面是两种常见的问题:

  1. 函数原型声明和函C定义不一?
  2. DLL导入函数时声明了不同的函数约?

以后者ؓ例,假设我们在dllU声明了一U函CؓQ?

__declspec(dllexport) int func(int a,int b);//注意Q这里没有stdcallQ用的是cdecl

使用时代码ؓQ?

 typedef int (*WINAPI DLLFUNC)func(int a,int b); hLib = LoadLibrary(...); DLLFUNC func = (DLLFUNC)GetProcAddress(...)//q里修改了调用约?result = func(1,2);//D错误 

׃调用者没有理解WINAPI的含义错误的增加了这个修饎ͼ上述代码必然D堆栈被破坏,MFC在编译时插入的checkesp函数告诉你Q堆栈被破坏了?/p>

]]>十一?MFC下的文gc?http://www.shnenglu.com/RancyGe/archive/2006/05/22/7496.html逃?/dc:creator>逃?/author>Mon, 22 May 2006 08:13:00 GMThttp://www.shnenglu.com/RancyGe/archive/2006/05/22/7496.htmlhttp://www.shnenglu.com/RancyGe/comments/7496.htmlhttp://www.shnenglu.com/RancyGe/archive/2006/05/22/7496.html#Feedback1http://www.shnenglu.com/RancyGe/comments/commentRss/7496.htmlhttp://www.shnenglu.com/RancyGe/services/trackbacks/7496.html1?a name="_Toc452640992">文g操作的方?/strong>

   使用Visual C++~程Q有如下Ҏq行文g操作Q?/font>

Q?Q用标准Cq行库函敎ͼ包括fopen、fclose、fseek{?/font>

   Q?Q用Win16下的文g和目录操作函敎ͼ如lopen、lclose、lseek{。不q,在Win32下,q些函数主要是ؓ了和Win16向后兼容?/font>

   Q?Q用Win32下的文g和目录操作函敎ͼ如CreateFileQCopyFileQDeleteFileQFindNextFileQ等{?/font>

      Win32下,打开和创建文仉由CreateFile完成Q成功的话,得到一个Win32下的句柄Q这不同于“C”的fopenq回的句柄。在Win16下,该句柄和Cq行库文件操作函数相宏V但在Win32下,“C”的文g操作函数不能使用该句柄,如果需要的话,可以使用函数_open_osfhandle从Win32句柄得到一个“C”文件函数可以用的文g句柄?/font> 关闭文g使用Win32的CloseHandle?/font> 在Win32下,CreateFile可以操作的对象除了磁盘文件外Q还包括讑֤文g如通讯端口、管道、控制台输入、邮件槽{等?/font>

Q?Q用CFile和其zc进行文件操作。CFile从CObjectzQ其zcd括操作文本文件的CStdioFileQ操作内存文件的CmemFileQ等{。CFile是徏立在Win32的文件操作体pȝ基础上,它封装了部分Win32文g操作函数。最好是使用CFilec(或派生类Q的对象来操作文Ӟ必要的话Q可以从q些cL生自q文g操作cR统一使用CFile的界面可以得到好的移植性?br />

2?a name="_Toc452640992">文g操作的方?
MFC用一些类来封装文件访问的Win32 API。以CFile为基Q从CFilez出几个类Q如CStdioFileQCMemFileQMFC内部使用的CMiororFileQ等{?br />

2.1、CFile的结?/b>

     2.1.1、CFile定义的枚丄?/b>

               CFilecd义了一些和文g操作相关的枚丄型,主要有四U:OpenFlagsQAttributeQSeekPositionQhFileNull。下面,分别解释q些枚Dcd?/p>

  1. OpenFlags

    OpenFlags定义?3U文件访问和׃n模式Q?/p>

    enum OpenFlags {

    //W一Q从叻I下同Q至W二位,打开文g时访问模式,??d

    modeRead = 0x0000,

    modeWrite = 0x0001,

    modeReadWrite = 0x0002,

    shareCompat = 0x0000, //32位MFC中没?/p>

    //W五到第七位Q打开文g时的׃n模式

    shareExclusive = 0x0010,//独占方式Q禁止其他进E读?/p>

    shareDenyWrite = 0x0020,//止其他q程?/p>

    shareDenyRead = 0x0030,//止其他q程?/p>

    shareDenyNone = 0x0040,//允许其他q程?/p>

    //W八位,打开文g时的文gl承方式

    modeNoInherit = 0x0080,//不允许子q程l承

    //W十三、十四位Q是否创建新文g和创建方?/p>

    modeCreate = 0x1000,//创徏新文Ӟ文g长度0

    modeNoTruncate = 0x2000,//创徏新文件时如文件已存在则打开

    //W十五、十六位Q文件以二进制或者文本方式打开Q在zcCStdioFile中用

    typeText = 0x4000,

    typeBinary = (int)0x8000

    };

  2. Attribute

    Attribute定义了文件属性:正常、只诅R隐含、系l文Ӟ文g或者目录等?/p>

    enum Attribute {

    normal = 0x00,

    readOnly = 0x01,

    hidden = 0x02,

    system = 0x04,

    volume = 0x08,

    directory = 0x10,

    archive = 0x20

    }

  3. SeekPosition

    SeekPosition定义了三U文件位|:头、尾、当前:

    enum SeekPosition{

    begin = 0x0,

    current = 0x1,

    end = 0x2

    };

  4. hFileNull

hFileNull定义了空文g句柄

enum { hFileNull = -1 };

2.1.2、CFile的其他一些成员变?/b>

CFile除了定义枚DcdQ还定义了一些成员变量。例如:

UINT m_hFile

该成员变量是public讉K属性,保存::CreateFileq回的操作系l的文g句柄。MFC重蝲了运符号HFILE来返回m_hFileQ这样在使用HFILEcd变量的地方可以用CFile对象?/p>

BOOL m_bCloseOnDelete;

CString m_strFileName;

q两个成员变量是protected讉K属性。m_bCloseOnDelete用来指示是否在关闭文件时删除CFile对象Qm_strFileName用来保存文g名?br />
2.1.3、CFile的成员函?/b>

CFile的成员函数实C对Win32文g操作函数的封装,完成以下动作Q打开、创建、关闭文Ӟ文g指针定位Q文件的锁定与解锁,文g状态的d和修改,{等。其中,用到了m_hFile文g句柄的一般是虚拟函数Q和此无关的一般是静态成员函数。一般地Q成员函数被映射到对应的Win32函数Q如?1-1所C?/p>

?1-1 CFile函数对Win32文g函数的封?/p>

虚拟

静?

成员函数

对应的Win32函数

文g的创建、打开、关?

?

 

Abort

CloseHandle

?

 

Duplicate

DuplicateHandle

?

 

Open

CreateFile

?

 

Close

CloseHandle

文g的读?

?

 

Read

ReadFile

   

ReadHugeQ向后兼容)

调用Read成员函数

?

 

Write

WriteFile

   

WriteHuage(向后兼容)

调用Write成员函数

?

 

Flush

FlushFileBuffers

文g定位

?

 

Seek

SetFilePointer

   

SeekToBegin

调用Seek成员函数

   

SeekToEnd

调用Seek成员函数

?

 

GetLength

调用Seek成员函数

?

 

SetLength

SetEndOfFile

文g的锁?解锁

?

 

LockRange

LockFile

?

 

UnlockRange

UnlockFile

文g状态操作函?

?

 

GetPosition

SetFilePointer

   

GetStatus(CFileStatus&)

GetFileTime,GetFileSize{?

 

?

GetStatus(LPSTR lpszFileName CFileStatus&)

FindFirstFile

?

 

GetFileName

不是单地映射到某个函?

?

 

GetFileTitle

 

?

 

GetFilePath

 

?

 

SetFilePath

 
 

?

SetStatus

 

改名和删?

 

?

Rename

MoveFile

 

?

Remove

DeleteFile


2.1.4、CFile的部分实?/b>

q里主要讨论CFile对象的构造函数和文g的打开/创徏的过E?/p>

  1. 构造函?

CFile有如下几个构造函敎ͼ

  • CFile()

~省构造函敎ͼ仅仅构造一个CFile对象Q还必须使用Open成员函数来打开文g?/p>

  • CFile(int hFile)

已经打开了一个文件hFileQ在此基上构造一个CFile对象来给它打包。HFile被赋值给CFile的成员变量m_hFile?/p>

  • CFile(LPCTSTR lpszFileName, UINT nOpenFlags)

指定一个文件名和文件打开方式Q构造CFile对象Q调用Open打开/创徏文gQ把文g句柄保存到m_hFile?/p>

  1. 打开/创徏文g

Open的原型如下:

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

CFileException* pException)

Open调用Win32函数::CreateFile打开文gQƈ把文件句柄保存到成员变量m_hFile中?/p>

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

);

昄QOpen必须把自q两个参数lpszFileName和nOpenFlags映射到CreateFile的七个参C?/p>

从OpenFlags的定义可以看出,QnOpenFlags & 3)表示了读写标识,映射成变量dwAccessQ可以取gؓWin32的GENERIC_READ、GENERIC_WRITE、GENERIC_READ|GENERIC_WRITE?/p>

QnOpenFlags & 0x70)表示了共享模式,映射成变量dwShareModeQ可以取gؓWin32的FILE_SHARE_READ、FILE_SHARE_WRITE、FILE_SHARE_WRITE|FILE_SHARE_READ?/p>

Open定义了一个局部的SECURITY_ATTRIBUTES变量saQ?nOpenFlags & 0x80)被赋值给sa.bInheritHandle?/p>

(nOpenFlags & modeCreate)表示了创建方式,映射成变量dwCreateFlagQ可以取gؓWin32的OPEN_ALWAYS、CREATE_ALWAYS、OPEN_EXISTING?/p>

在生成了上述参数之后Q先调用::CreateFileQ?/p>

HANDLE hFile =::CreateFile(lpszFileName,

dwAccess, dwShareMode, &sa,

dwCreateFlag, FILE_ATTRIBUTE_NORMAL, NULL);

然后QhFile被赋值给成员变量m_hFileQm_bCloseOnDelete被设|ؓTRUE?/p>

׃可以看出QCFile打开(创徏)一个文件时大大化了:: CreateFile函数的复杂性,卛_需要指定一个文件名、一个打开文g的参数即可。若该参数指定ؓ0Q则表示以只L式打开一个存在的文gQ独占用,不允许子q程l承?/p>

在CFile对象使用Ӟ如果它是在堆中分配的Q则应该销毁它Q如果在栈中分配的,则CFile对象被自动销毁。销毁时析构函数被调用,析构函数是虚拟函数。若m_bCloseOnDelete为真且m_hFile非空Q则析构函数调用Close关闭文g?/p>

至于其他CFile成员函数的实玎ͼq里不作分析了?/p>

2.1.5、CFile的派生类

q里主要要地介绍CStdioFile和CmemFile及CFileFind?/p>

  1. CStdioFile

    CStdioFileҎ本文件进行操作?/p>

    CStdioFile定义了新的成员变量m_pStreamQ类型是FILE*。在打开或者创建文件时Q用_open_osfhandle从m_hFile(Win32文g句柄)得到一个“C”的FILEcd的文件指针,然后Q在文g操作中,使用“C”的文g操作函数。例如,L件用_freadQ而不?:ReadFileQ写文g使用了_fwriteQ而不?:WriteFileQ等{。m_hFile是CFile的成员变量?/p>

    另外QCStdioFile不支持CFile的Dumplicate、LockRange、UnlockRange操作Q但是实C两个新的操作ReadString和WriteString?/p>

  2. CMemFile

    CMemFile把一块内存当作一个文件来操作Q所以,它没有打开文g的操作,而是设计了Attach和Detach用来分配或者释放一块内存。相应地Q它提供了Alloc、Free虚拟函数来操作内存文Ӟ它覆盖了Read、Write来读写内存文件?/p>

  3. CFileFind

Z方便文g查找QMFC把有兛_能归l成Z个类CFileFind。CFileFindz于CObjectcR首先,它用FindFile和FineNextFile包装了Win32函数::FindFirstFile?:FindNextFileQ其ơ,它提供了许多函数用来获取文g的状态或者属性?/p>

使用CFileStatusl构来描q文件的属性,其定义如下:

struct CFileStatus

{

CTime m_ctime; // 文g创徏旉

CTime m_mtime; // 文g最q一ơ修Ҏ?/p>

CTime m_atime; // 文g最q一ơ访问时?/p>

LONG m_size; // 文g大小

BYTE m_attribute; // 文g属?/p>

BYTE _m_padding; // 没有实际含义Q用来增加一个字?/p>

TCHAR m_szFullName[_MAX_PATH]; //l对路径

#ifdef _DEBUG

//实现Dump虚拟函数Q输出文件属?/p>

void Dump(CDumpContext& dc) const;

#endif

};

例如Q?/p>

CFileStatus status;

pFile->GetStatus(status);

#ifdef _DEBUG

status.dump(afxDump);

#endif

 



]]>
十、内存分配方式和调试机制 http://www.shnenglu.com/RancyGe/archive/2006/04/17/5757.html逃?/dc:creator>逃?/author>Mon, 17 Apr 2006 08:09:00 GMThttp://www.shnenglu.com/RancyGe/archive/2006/04/17/5757.htmlhttp://www.shnenglu.com/RancyGe/comments/5757.htmlhttp://www.shnenglu.com/RancyGe/archive/2006/04/17/5757.html#Feedback2http://www.shnenglu.com/RancyGe/comments/commentRss/5757.htmlhttp://www.shnenglu.com/RancyGe/services/trackbacks/5757.html 10.1内存分配 
  10.1.1 内存分配函数

MFCWin32或者C语言的内存分配APIQ有四种内存分配API可供使用?

  1. Win32的堆分配函数

    每一个进E都可以使用堆分配函数创Z个私有的堆──调用q程地址I间的一个或者多个页面。DLL创徏的私有堆必定在调用DLL的进E的地址I间内,只能被调用进E访问?

    HeapCreate用来创徏堆;HeapAlloc用来从堆中分配一定数量的I间QHeapAlloc分配的内存是不能Ud的;HeapSize可以定从堆中分配的I间的大;HeapFree用来释放从堆中分配的I间QHeapDestroy销毁创建的堆?

  2. Windows传统的全局或者局部内存分配函?

    ׃Win32采用q面内存l构模式QWin32下的全局和局部内存函数除了名字不同外Q其他完全相同。Q一函数都可以用来分配Q意大的内存Q仅仅受可用物理内存的限Ӟ。用法可以和Win16下基本一栗?

    Win32下保留这cdC证了和Win16的兼宏V?

  3. C语言的标准内存分配函?

    C语言的标准内存分配函数包括以下函敎ͼ

    mallocQcallocQreallocQfreeQ等?

    q些函数最后都映射成堆API函数Q所以,malloc分配的内存是不能Ud的。这些函数的调式版本?

    malloc_dbgQcalloc_dbgQrealloc_dbgQfree_dbgQ等?

  4. Win32的虚拟内存分配函?

虚拟内存API是其他API的基。虚拟内存API以页为最分配单位,X86上页长度?KBQ可以用GetSystemInfo函数提取长度。虚拟内存分配函数包括以下函敎ͼ

  • LPVOID VirtualAlloc(LPVOID lpvAddress,

DWORD cbSize,

DWORD fdwAllocationType,

DWORD fdwProtect);

该函数用来分配一定范围的虚拟c参?指定起始地址Q参?指定分配内存的长度;参数3指定分配方式Q取值MEM_COMMINT或者MEM_RESERVEQ参?指定控制讉K本次分配的内存的标识Q取gؓPAGE_READONLY、PAGE_READWRITE或者PAGE_NOACCESS?

  • LPVOID VirtualAllocEx(HANDLE process,

LPVOID lpvAddress,

DWORD cbSize,

DWORD fdwAllocationType,

DWORD fdwProtect);

该函数功能类gVirtualAllocQ但是允许指定进Eprocess。VirtaulFree、VirtualProtect、VirtualQuery都有对应的扩展函数?

  • BOOL VirtualFree(LPVOID lpvAddress,

DWORD dwSize,

DWORD dwFreeType);

该函数用来回收或者释攑ֈ配的虚拟内存。参?指定希望回收或者释攑ֆ存的基地址Q如果是回收Q参?可以指向虚拟地址范围内的M地方Q如果是释放Q参?必须是VirtualAllocq回的地址Q参?指定是否释放或者回收内存,取gؓMEM_DECOMMINT或者MEM_RELEASE?

  • BOOL VirtualProtect(LPVOID lpvAddress,

DWORD cbSize,

DWORD fdwNewProtect,

PDWORD pfdwOldProtect);

该函数用来把已经分配的页改变成保护页。参?指定分配늚基地址Q参?指定保护늚长度Q参?指定늚保护属性,取值PAGE_READ、PAGE_WRITE、PAGE_READWRITE{等Q参?用来q回原来的保护属性?

  • DWORD VirtualQuery(LPCVOID lpAddress,

PMEMORY_BASIC_INFORMATION lpBuffer,

DWORD dwLength

);

该函数用来查询内存中指定늚Ҏ。参?指向希望查询的虚拟地址Q参?是指向内存基本信息结构的指针Q参?指定查询的长度?

  • BOOL VirtualLock(LPVOID lpAddress,DWORD dwSize);

该函数用来锁定内存,锁定的内存页不能交换到页文g。参?指定要锁定内存的起始地址Q参?指定锁定的长度?

  • BOOL VirtualUnLock(LPVOID lpAddress,DWORD dwSize);

参数1指定要解锁的内存的v始地址Q参?指定要解锁的内存的长度?br />

C++的new ?delete操作W?

MFC定义了两U作用范围的new和delete操作W。对于newQ不论哪U,参数1cd必须是size_tQ且q回voidcd指针?

  1. 全局范围内的new和delete操作W?

    原型如下Q?

    void _cdecl ::operator new(size_t nSize);

    void __cdecl operator delete(void* p);

    调试版本Q?

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

    LPCSTR lpszFileName, int nLine)

  2. cd义的new和delete操作W?

原型如下Q?

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

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

cȝoperator new操作W是cȝ静态成员函敎ͼ对该cȝ对象来说覆盖全局的operator new。全局的operator new用来l内部类型对象(如intQ、没有定义operator new操作W的cȝ对象分配内存?

new操作W被映射成malloc或者malloc_dbgQdelete被映成free或者free_dbg?



10.1.2调试手段

MFC应用E序可以使用Cq行库的调试手段Q也可以使用MFC提供的调试手Dc两U调试手D分别论q如下?br />

  1. Cq行库提供和支持的调试功?/b>

    Cq行库提供和支持的调试功能如下:

    1. 调试信息报告函数

      用来报告应用E序的调试版本运行时的警告和出错信息。包括:

      _CrtDbgReport 用来报告调试信息Q?/p>

      _CrtSetReportMode 讄是否警告、出错或者断a信息Q?/p>

      _CrtSetReportFile 讄是否把调试信息写入到一个文件?/p>

    2. 条g验证或者断a宏:

      断言宏主要有Q?/p>

      assert 验某个条件是否满I不满终止程序执行?/p>

      验证函数主要有:

      _CrtIsValidHeapPointer 验证某个指针是否在本地堆中;

      _CrtIsValidPointer 验证指定范围的内存是否可以读写;

      _CrtIsMemoryBlock 验证某个内存块是否在本地堆中?/p>

    3. 内存Q堆Q调试:

    malloc_dbg 分配内存时保存有兛_存分配的信息Q如在什么文件、哪一行分配的内存{。有一pd用来提供内存诊断的函敎ͼ

    _CrtMemCheckpoint 保存内存快照在一个_CrtMemStatel构中;

    _CrtMemDifference 比较两个_CrtMemStateQ?/p>

    _CrtMemDumpStatistics 转储输出一_CrtMemStatel构的内容;

    _CrtMemDumpAllObjectsSince 输出上次快照或程序开始执行以来在堆中分配的所有对象的信息Q?/p>

    _CrtDumpMemoryLeaks 程序执行以来的内存漏洞Q如果有漏洞则输出所有分配的对象?/p>

    2.      MFC提供的调试手D?/b>

    MFC在Cq行库提供和支持的调试功能基上,设计了一些类、函数等来协助调试?/p>

    1. MFC的TRACE、ASSERT

      ASSERT

      使用ASSERT断言判定E序是否可以l箋执行?/p>

      TRACE

      使用TRACE宏显C或者打印调试信息。TRACE是通过函数AfxTrace实现的。由于AfxTrace函数使用了cdecl调用U定Q故可以接受个数不定的参敎ͼ如同printf函数一栗它的定义和实现如下Q?/p>

      void AFX_CDECL AfxTrace(LPCTSTR lpszFormat, ...)

      {

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

      if (!afxTraceEnabled)

      return;

      #endif

      //处理个数不定的参?/p>

      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

      在程序源码中Q可以控制是否显Ct信息,昄什么跟t信息。如果全局变量afxTraceEnabled为TRUEQ则TRACE宏可以输出;否则Q没有TRACE信息被输出。如果通过afxTraceFlags指定了跟t什么消息,则输出有兌t信息,例如Z指定“Multilple Application Debug”,令AfxTraceFlags|=traceMultiApp。可以跟t的信息有:

      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

      };

      q样Q应用程序可以在需要的地方指定afxTraceEnabled的值打开或者关闭TRACE开养I指定AfxTraceFlags的D滤跟t信息?/p>

      Visual C++提供了一个TRACE工具Q也可以用来完成上述功能?/p>

      Z昄消息信息QMFC内部定义了一个AFX_MAP_MESSAGcd的数lallMessagesQ储存了Windows消息和消息名映射寏V例如:

      allMessages[1].nMsg = WM_CREATE,

      allMessages[1].lpszMsg = “WM_CREATE?/p>

      MFC内部q用函数_AfxTraceMsg昄跟踪消息Q它可以接收一个字W串和一个MSG指针Q然后,把该字符串和MSG的各个域的信息组合成一个大的字W串q用AfxTrace昄出来?/p>

      allMessages和函数_AfxTraceMsg的详l实现可以参见AfxTrace.cpp?/p>

    2. MFC对象内容转储

      对象内容转储是CObjectcL供的功能Q所有从它派生的c都可以通过覆盖虚拟函数DUMP来支持该功能。在讲述CObjectcL曾提到过?/p>

      虚拟函数Dump的定义:

      class ClassName : public CObject

      {

      public:

      #ifdef _DEBUG

      virtual void Dump( CDumpContext& dc ) const;

      #endif

      ?/p>

      };

      在用DumpӞ必须l它提供一个CDumpContextcd的参敎ͼ该参数指定的对象负责输试信息。ؓ此,MFC提供了一个预定义的全局CDumpContext对象afxDumpQ它把调试信息输送给调试器的调试H口。从前面AfxTrace的实现可以知道,MFC使用了afxDump输出跟踪信息到调试窗口?/p>

      CDumpContextcL有基c,它提供了以文本Ş式输断信息的功能?/p>

      例如Q?/p>

      CPerson* pMyPerson = new CPerson;

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

      //...

      // now dump the contents

      #ifdef _DEBUG

      pMyPerson->Dump( afxDump );

      #endif

    3. MFC对象有效性检?

    对象有效性检是CObjectcL供的功能Q所有从它派生的c都可以通过覆盖虚拟函数AssertValid来支持该功能。在讲述CObjectcL曾提到过?/p>

    虚拟函数AssertValid的定义:

    class ClassName : public CObject

    {

    public:

    #ifdef _DEBUG

    virtual void AssertValid( ) const;

    #endif

    ?

    };

    使用ASSERT_VALID宏判断一个对象是否有效,该对象的cd覆盖了AssertValid函数。Ş式ؓQASSERT_VALID(pObject)?/p>

    另外QMFC提供了一些函数来判断地址是否有效Q如Q?/p>

    AfxIsMemoryBlockQAfxIsStringQAfxIsValidAddress?/p>

    10.1.3内存诊断

    MFC使用DEBUG_NEW来跟t内存分配时的执行的源码文g和行数?/p>

    ?define new DEBUG_NEW插入到每一个源文g中,q样Q调试版本就使用_malloc_dbg来分配内存。MFC Appwizard在创建框架文件时已经作了q样的处理?/p>

    1. AfxDoForAllObjects

      MFC提供了函数AfxDoForAllObjects来追t动态分配的内存对象Q函数原型如下:

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

      void* pContext), void* pContext );

      其中Q?/p>

      参数1是一个函数指针,AfxDoForAllObjectsҎ个对象调用该指针表示的函数?/p>

      参数2传递给参数1指定的函数?/p>

      AfxDoForAllObjects可以到所有用new分配的CObject对象或者CObjectcL生的对象Q但全局对象、嵌入对象和栈中分配的对象除外?/p>

    10.1.4内存漏洞?/strong>

    仅仅用于new的DEBUG版本分配的内存?/p>

    完成内存漏洞,需要如下系列步骤:

    • 调用AfxEnableMemoryTracking(TRUE/FALSE)打开/关闭内存诊断。在调试版本下,~省是打开的;关闭内存诊断可以加快E序执行速度Q减诊断输出?

    • 使用MFC全局变量afxMemDF更精地指定诊断输出的特征,~省值是allocMemDFQ可以取如下值或者这些值相或:

    afxMemDFQdelayFreeMemDFQcheckAlwaysMemDF

    其中QallocMemDF表示可以q行内存诊断输出QdelayFreeMemDF表示是否是在应用E序l束时才调用free或者deleteQ这样导致程序最大可能的分配内存QcheckAlwaysMemDF表示每一ơ分配或者释攑ֆ存之后都调用函数AfxCheckMemoryq行内存(AfxCheckMemory查堆中所有通过new分配的内存(不含mallocQ)?/p>

    q一步是可选步骤,非必R?/p>

    • 创徏一个CMemStatecd的变量oldMemStateQ调用CMemState的成员函数CheckPoint获得初次内存快照?

    • 执行了系列内存分配或者释放之后,创徏另一个CMemStatecd变量newMemStateQ调用CMemState的成员函数CheckPoint获得新的内存快照?

    • 创徏W三个CMemStatecd变量difMemStateQ调用CMemState的成员函数Difference比较oldMemState和newMemStateQ结果保存在变量difMemState中。如果没有不同,则返回FALSEQ否则返回TRUE?

    • 如果不同Q则调用成员函数DumpStatistics输出比较l果?

    例如Q?/p>

    // 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在应用程序(调试版)l束Ӟ自动q行内存漏洞,如果存在漏洞Q则输出漏洞的有关信息?/p>



]]>
þԭav| þרƷ| ھƷþ| ŷ츾BBBþþ| þþž޾Ʒ| þþþ޾Ʒþþþþþ| þùƷ͵99| ɫۺϾþþþר| þۺĻ| ҹƷþþþþ| þ99Ʒþþþ| þ97þ97Ʒӿ| ٸþþþþñŪ߳| ۿ ۺϾþþþùɫ ŷ һ | ݹƷþþþ| ޹˾Ʒþþþþۺ | պƷþþþþ| ھƷþþþþþӰ鶹 | þøƬ| þ91Ʒ91þû| þþƷþһ| Ļһþվ| þAV뾫Ʒɫҹ| Ʒþþþ龫Ʒ | þþۺϾɫۺ98| Ⱦþԭɫwww| 91Ʒþþþþ| 뾫Ʒþþþ.. | ɫվþav| ƷþĻ| ձƬҹþ| þˬˬƬav | ŷþ޾Ʒ| Ʒ99þþþ| 91鶹Ʒ91þþþþ | ƷۺϾþþþþ888ѿ| þþþþùƷŮ| þݺҹҹ| þþþĻɫ | ŵþ| þþƷĻ|