⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chapter5.html

📁 WRITING BUG-FREE C CODE.
💻 HTML
📖 第 1 页 / 共 3 页
字号:
} /* VerifyHeapPointer */</pre></tr></td></table> <br>VerifyHeapPointer() code first checks to determine whether the pointer passed to it is NULL or not.  Next, the lpMem pointer is validated to make sure that it is a valid pointer into the heap.  This is done by calling FmIsPtrOk().  Then, a pointer to the prefix structure is obtained and the memory pointer in the structure is checked against the memory pointer being validated.  Finally, the prefix pointer in the postfix structure is validated.  Most types of memory overwrites past the end of an object trash the prefix pointer in the postfix structure.  This is how memory overwrites are detected. <br><br><b>5.2.8 FmIsPtrOk() Implementation</b><br><br>The VerifyHeapPointer() must be called only by the heap management functions and is a local function to the heap management module.  This function is complete except for the FmIsPtrOk() function.  FmIsPtrOk() validates that a pointer passed to it is a realistic heap pointer.   <blockquote><table bgcolor="#E0E0E0" border=1 cellpadding=2 cellspacing=0><tr><td>   You must code a FmIsPtrOk() function that is appropriate for your   environment.   </tr></td></table></blockquote><table bgcolor="#F0F0F0"><tr><td><img src="./windows.gif"> The FmIsPtrOk() function is defined as follows. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>FmIsPtrOk() function, for Intel segmented protected-mode application</b>BOOL APIENTRY FmIsPtrOk( LPVOID lpMem ){    BOOL bOk=FALSE;    _asm xor ax, ax                  ;; assume bad selector    _asm lsl ax, word ptr [lpMem+2]  ;; get selector limit    _asm cmp word ptr [lpMem], ax    ;; is ptr offset under limit    _asm jae done                    ;; no, bad pointer    _asm mov bOk, 1                  ;; yes, pointer OK    _asm done:    return (bOk);} /* FmIsPtrOk */</pre></tr></td></table> <br>The key to this implementation of the FmIsPtrOk() function is the usage of lsl.  This assembly instruction loads a register (in this case, register ax) with the limit of the provided selector (in this case, the selector of lpMem).  If the offset of the lpMem pointer is within the limit of the memory segment pointed to by the selector of lpMem, the lpMem memory pointer is considered valid.</td></tr></table><br>For a non-protected-mode application, the FmIsPtrOk() function is as follows. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>FmIsPtrOk() function, for non-protected-mode application</b>BOOL APIENTRY FmIsPtrOk( LPVOID lpMem ){    return ((lpMem) && (!((long)lpMem&(ALIGNMENT-1))));} /* FmIsPtrOk */</pre></tr></td></table> <br>In a non-protected-mode environment, FmIsPtrOk() can assume only that the pointer is OK if the pointer is non-NULL and properly aligned. <br><br><a name="fmwalkheap"></a><b>5.2.9 FmWalkHeap() Implementation</b> <br><br>An important part of any heap manager is the ability to display a symbolic heap dump. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>FmWalkHeap() function</b>void APIENTRY FmWalkHeap( void ){    if (lpHeapHead) {        LPPREFIX lpCur=lpHeapHead;        while (VerifyHeapPointer(&lpCur[1])) {            char buffer[100];            RenderDesc( lpCur, buffer );            /*--- print out buffer ---*/            /* printf( "walk: %s\n", buffer ); */            lpCur = lpCur-&gt;lpNext;            if (lpCur==lpHeapHead) {                break;                }            }        }} /* FmWalkHeap */</pre></tr></td></table> <br>The implementation of FmWalkHeap() is pretty straightforward.  It walks the heap getting descriptions of objects and prints them out.  The implementation of getting a description of an object is left to RenderDesc(). <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>RenderDesc() function</b>void LOCAL RenderDesc( LPPREFIX lpPrefix, LPSTR lpBuffer ){    if (lpPrefix-&gt;lpMem==&lpPrefix[1]) {        sprintf( lpBuffer, "%08lx ", lpPrefix );        if (lpPrefix-&gt;lpFilename) {            sprintf( lpBuffer+strlen(lpBuffer), "%12s %4ld ",                lpPrefix-&gt;lpFilename, lpPrefix-&gt;lLineNumber );            }        if (lpPrefix-&gt;lpClassDesc) {            sprintf( lpBuffer+strlen(lpBuffer), "%s",                lpPrefix-&gt;lpClassDesc-&gt;lpVarName );            }        }    else {        strcpy( lpBuffer, "(bad)" );        }} /* RenderDesc */</pre></tr></td></table> <br>This is one possible implementation of RenderDesc().  It displays the address of an object, its filename and line number, if any, and its class descriptor name, if any. <br><br><b>5.2.10 FmRealloc() Implementation</b> <br><br>Another memory management function that needs a new interface is realloc().  While not presented here, the source for FmRealloc() can be found in the Code Listings Appendix.<br><a name="stringinterface"><br></a><big><b>5.3 An Interface for Strings</b></big> <br><br>How should strings be dynamically allocated from the heap?  Who is responsible for calling the heap interface?  The best way to approach this problem is through the utilization of an abstraction layer.  It is best to implement the string interface through a set of macros. <br><br><b>5.3.1 NEWSTRING() Macro</b> <br><br>The first macro is NEWSTRING().  It is used to allocate storage for a string from the heap. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>NEWSTRING() macro</b>#define NEWSTRING(lpDst,wSize) \  (_LPV(lpDst)=FmNew((SIZET)(wSize),NULL,szSRCFILE,__LINE__))</pre></tr></td></table> <br>NEWSTRING() can almost be considered a replacement for malloc().  The macro takes two arguments.  The first argument is the name of a variable that is to contain the pointer of the allocated block of memory.  The second argument is the size in bytes of the block of memory to allocate.  This macro was designed this way so that the _LPV() macro could be used to avoid type casting of the pointer returned from FmNew(). <br><br>To free the memory allocated through NEWSTRING(), simply call FREE(). <br><br><b>5.3.2 MYLSTRDUP() Macro</b> <br><br>The second macro is MYLSTRDUP().  It is a replacement for strdup() that uses the new heap manager interface. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>MYLSTRDUP() macro</b>#define MYLSTRDUP(lpDst,lpSrc) \  (_LPV(lpDst)=FmStrDup(lpSrc,szSRCFILE,__LINE__))</pre></tr></td></table> <br>MYLSTRDUP() is a macro interface around FmStrDup().  It is a new heap manager function that does the dirty work of duplicating the string. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>FmStrDup()</b>LPVOID APIENTRY FmStrDup( LPSTR lpS, LPSTR lpFile, int nLine ){    LPVOID lpReturn=NULL;    if (lpS) {        SIZET wSize = (SIZET)(strlen(lpS)+1);        lpReturn = FmNew( wSize, NULL, lpFile, nLine );        if (lpReturn) {            memcpy( lpReturn, lpS, wSize );            }        }    return(lpReturn);} /* FmStrDup */</pre></tr></td></table> <br>If a NULL pointer is passed into FmStrDup(), a NULL pointer is returned.  FmStrDup() works by first calculating the string length and then adding one for the null character.  This number of bytes is then allocated by calling FmNew() and finally the string is copied into the new memory buffer.<br><a name="arrayinterface"><br></a><big><b>5.4 An Interface for Arrays</b></big> <br><br>Just as with strings, using macros as a level ob abstraction greatly simplifies using arrays. <br><br><b>5.4.1 NEWARRAY() Macro</b> <br><br>The NEWARRAY() macro is used to create a new array with a specific number of array elements. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>NEWARRAY macro</b>#define NEWARRAY(lpArray, wSize) \  (_LPV(lpArray)=FmNew((SIZET)(sizeof(*(lpArray))*(wSize)), \  NULL,szSRCFILE,__LINE__))</pre></tr></td></table> <br>NEWARRAY() takes as its first argument the name of the variable that is to contain the pointer of the allocated array.  The second argument is the number of array elements (not bytes) to allocate. <br><br>To free the memory allocated through NEWARRAY(), simply call FREE(). <br><br><b>5.4.2 SIZEARRAY() Macro</b> <br><br>The SIZEARRAY() macro is used to resize an array. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>SIZEARRAY Macro</b>#define SIZEARRAY(lpArray, wSize) \  (_LPV(lpArray)=FmRealloc((lpArray), \  (SIZET)(sizeof(*(lpArray))*(wSize)),szSRCFILE,__LINE__))</pre></tr></td></table> <br>SIZEARRAY() takes as its first argument the name of the variable that points to an allocated array.  If it is NULL, SIZEARRAY() allocates a new array.  The second argument is the new size of the array in array elements (not bytes).<br><a name="detectleaks"><br></a><big><b>5.5 Detecting Storage Leaks</b></big> <br><br>If a program allocates an object from the heap, it should also free the object before the program exits.  If the object is not freed, it is an error and is called a storage leak. <br><br>Do you have any storage leaks in the programs that you write?  How do you detect storage leaks?  Unless you have a formal methodology in place, how do you really know? <br><br>I once wrote a large program that did not have any formal storage leak detection.  The program had no problems and I would have claimed that there were no storage leaks present.  However, adding storage leak detection actually turned up a few obscure leaks.  They were so obscure that no matter how long the program was run, the storage leaks would never have caused a problem and this is why they were never found. <br><br>No matter how small or large a program you write, the program should always have some form of storage leak detection in place.   <blockquote><table bgcolor="#E0E0E0" border=1 cellpadding=2 cellspacing=0><tr><td>   Storage leak detection should always be in place.   </tr></td></table></blockquote>The simplest way to detect storage leaks is to call <a href="#fmwalkheap">FmWalkHeap()</a> the moment your program is about to exit.  You will then see exactly what objects are left in the heap, if any.  Because the filename and line number where the object are allocated is actually displayed, tracking down the storage leak is a snap.<br><a name="windowsmemory"><br></a><table bgcolor="#F0F0F0"><tr><td><img src="./windows.gif"> <big><b>5.6 Windows Memory Model Issues</b></big> <br><br>There is a long-standing problem in the Windows environment as to which memory model should be used for developing a program.  It is complicated by the fact that most compilers for Windows support four basic memory models.  They are small, medium, compact and large.  However, the choice is often between the medium memory model and the large memory model because small and compact are too limiting. <br><br><img src="./windows.gif"> <b>5.6.1 The Large Memory Model Myth</b> <br><br>It is widely believed that the large memory model should not be used for Windows programming.  This was true at one time, but today it is a myth! <br><br>In Windows, if a program contains more than one data segment, it can be run only once, not multiple times.  There is also a limit on how many memory allocations can be made.  This limit is around 4,096 or 8,192, depending upon how Windows was run (standard versus enhanced-mode).  This limit is actually a hardware limit in how many segments it has to manage. <br><br>These limits existed in Microsoft C6, but beginning with Microsoft C7, these limits were removed.  The limits existed due to how the compiler and the run-time libraries of C6 were written. <br><br>In C6 with the large memory model, each source file would gets its own data segment.  However, starting with C7 only one data segment is produced if possible.  So this solves the data segment problem. <br><br>Concerning the limit on the number of memory allocations, C6 used to eat up a segment on every memory allocation.  Starting with C7, the run-time library now employs a subsegment allocation scheme which all but eliminates the problem. <br><br>The bottom line is that if you do not mind the overhead of using far pointers extensively (instead of near pointers), go ahead and use the large memory model.  It sure makes life a lot easier. <br><br>For a more in-depth discussion on the large memory model myth, I suggest you read Windows Internals by Matt Pietrek.</td></tr></table><br><a name="summary"><br></a><big><b>5.7 Chapter Summary</b></big> <br><br> <ul type="disc"><li>The problem with traditional heap managers is that they assume the programmer never makes a mistake in calling the interface.  The traditional heap manager is simply not robust enough for your needs.  The solution is to code a module that provides a code wrapper around the existing heap manger.<li>Since the new heap manager must also meet the needs of the class methodology and run-time object verification, every block of memory that is allocated through the new heap manager interface is prefixed and postfixed by a structure.<li>NEWSTRING() and MYLSTRDUP() provide a new interface for strings.  NEWARRAY() and SIZEARRAY() provide a new interface for arrays.<li>Storage leak detection must always be in place in the programs you write.  The simplest way to detect storage leaks is to call FmWalkHeap() the moment your program is about to exit. </ul> <br><br><hr><center><small>Copyright &copy; 1993-1995, 2002 Jerry Jongerius<br>This book was previously published by Person Education, Inc.,<br>formerly known as Prentice Hall. ISBN: 0-13-183898-9<br></small></center></html></body>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -