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

📄 chapter6.html

📁 Writing Bug-Free C Code
💻 HTML
📖 第 1 页 / 共 3 页
字号:
 <br><br>This traditional technique works for small projects, but it does not scale to large projects well, because before you know it, you end up with a lot of include files and a lot of interdependencies between them.  To avoid headaches you end up including almost every include file in every source file.  At least that is what happened to me.  Also, compilation times got longer and longer. <br><br>Using the <a href="chapter4.html">class mechanism described in Chapter 4</a> eliminated the data interdependency problem, but it still left me with long compile-times and a lot of include files.  How could I eliminate all the include files and speed up the compile-times? <br><br>As the project I was working on got larger and larger, I began to notice something interesting.  The include file model for a small project and a large project are different. <br><br>A small project tends to have a few source files with a lot of functions that perform a lot of varied tasks.  This requires a lot of include files in each source file. <br><br>A large project tends to have highly specialized modules that contain a few method functions and a few internal support functions which implement a specific class object.  This requires just a few include files in each source file.   <blockquote><table bgcolor="#E0E0E0" border=1 cellpadding=2 cellspacing=0><tr><td>   The include file model for a small project and a large project are   different.   </tr></td></table></blockquote>The solution to all my problems came in the form of one global include file with all but a few sections enclosed in #ifdef USE_HOBJ / #endif sections.  At the top of the global include file are all NEWHANDLE() declarations and function prototypes to commonly used system level code.  Within each #ifdef USE_HOBJ / #endif section are the prototypes for the HOBJ class, where HOBJ is a place holder for the name of the class. <br><br>At the top of every module is a list of #define's enumerating what that module uses.  Following this is a #include of the global include file. <br><br>The combination of the class mechanism and the new USE_* model caused the compilation times to improve dramatically and reduced the number of include files to just one. <br><br><b>6.5.1 Precompiled Headers Surprise</b> <br><br>By now, many of you are probably wondering why precompiled headers were not used instead?  Well, I tried them.  They increased the build time of my large project by 50% percent.  I was surprised, but the reasons make sense. <br><br>My project is huge with all modules being specialized.  Because of this, each module is really not including that much because of the USE_* include model.  However, the precompiled header that was being used contained the include information that was needed by all modules.  The precompiled header was huge, but the compiler was able to load it fast and start compiling the source file right away.  So the extra build time was not due to the huge precompiled header size.  Then why was the build time of the project 50 percent longer? <br><br>My best guess is that the huge precompiled header was causing longer symbol table search times within the compiler.  Without the precompiled header, only a few sections of the include file were being included, causing the compiler symbol table to be practically empty and short symbol table search times.<br><a name="coding"><br></a><big><b>6.6 Coding the Module</b></big> <br><br>Now that the module interface has been designed, it is time to start coding the module.  It is not enough that a module be coded bug-free.  It must also look good and be documented well.  The true test of a well-written module is if your coworkers can take that module and read it, understanding everything that is going on without any help from you. <br><br>What looks good is highly subjective.  However, I highly recommend that you pick some documenting style that works for you and subject the style to a review by your coworkers.  After all, you have to read and modify their code and they have to read and modify your code! <br><br><b>6.6.1 The Copyright Header</b> <br><br>At the top of every module (source file), there should be a copyright notice.  Where I work, it looks something like the following. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>The copyright header</b>/****************************************************************//*                                                              *//*                        (project name)                        *//*                                                              *//*    Copyright (date) (Company Name). All rights reserved.     *//*                                                              *//*    This program contains the confidential trade secret       *//*    information of (Company Name).  Use, disclosure, or       *//*    copying without written consent is strictly prohibited.   *//*                                                              *//****************************************************************/</pre></tr></td></table><br>(Company Name), (project name) and (date) are place holders to be filled in by you. <br><br><b>6.6.2 Module Documentation</b> <br><br>Following the copyright header is a comment section that describes the module as a whole. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>The module comment header</b>/*pm-------------------------------------------------------------- * *  OUTLINE: * *    (Describes the purpose of the module) * *  IMPLEMENTATION: * *    (Describes in high level terms how the module works) * *  NOTES: * *    (Enumerate noteworthy items) * *--------------------------------------------------------------*/</pre></tr></td></table> <br>The OUTLINE section.  Use this section to describe why the module needs to exist.  What is it doing?  Pretend that a coworker walked up to you and asked what you are working on. <br><br>The IMPLEMENTATION section.  Use this section to spell out the major algorithms that you are going to use to implement the module.  Again, pretend that a coworker asked you how you are going to implement the module that you just described to him or her. <br><br>The NOTES section.  This section is a catchall section where you can put anything you want.  I use this section for notes that would be helpful to anyone who has to modify the code months down the road.  Another use is to document special situations that must be tested before the modified code can be checked back into a version control system. <br><br>Usage of pm in the comment header is used by an <a href="chapter7.html#doctools">automatic documentation tool</a>. <br><br><b>6.6.3 Include File Section and USEWINASSERT</b> <br><br>Following the module documentation is a series of #define USE_'s followed by the #include of the global include file and USEWINASSERT. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Include section example</b>#define USE_HRAND#define USE_HDOSFH#include "app.h"USEWINASSERT</pre></tr></td></table> <br>A module always has at least one #define USE_*, because you always want #included the section of the include file for the module you are working on. <br><br><b>6.6.4 The Class Declaration</b> <br><br>What follows next is usually a class declaration for the object that is being implemented by the module.  The class declaration for the random number generator looks like the following. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Random number generator class declaration</b>CLASS(hRand, HRAND) {    long lRand;    };</pre></tr></td></table> <br><b>6.6.5 Prototypes of LOCAL Functions</b> <br><br>Following the class declaration are the prototypes for functions that are used and defined only in this module.  It is important for proper error checking by the compiler that every function be prototyped before being used or called.   <blockquote><table bgcolor="#E0E0E0" border=1 cellpadding=2 cellspacing=0><tr><td>   Every function in the module must be prototyped.   </tr></td></table></blockquote>The method functions of the module are prototyped in the global include file and the proper #define USE_* causes them to be included.  The functions that are local to this module must not be prototyped in the global include file because they are not part of the module interface that is called by other modules.  Instead, they are private to the module and prototyped in the module. <br><br><b>6.6.6 APIENTRY (Method) Functions</b> <br><br>I usually organize my module files so that all the functions that are entry points into the module appear at the top of the source file and all local functions appear after the entry point functions. <br><br>It is important to provide a comment header for every single function in the module that properly documents the functions.  Where I work, a comment header that looks like the following is used. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>The function comment header template</b>/*pf-------------------------------------------------------------- * *  DESCRIPTION: (A few word description) initials * *    (A long description of the function) * *  ARGUMENTS: * *    Arg1 - Arg1 description *    ... *    ArgN - ArgN description * *  RETURNS: * *    (A description of the return value) * *  NOTES: * *    (optional notes section) * *--------------------------------------------------------------*/</pre></tr></td></table> <br>The DESCRIPTION section.  The section starts off with a terse description of the function in parentheses.  Following this are the initials of the programmer(s) who worked on this function.  The body of this section contains a sentence or two that describe what the function does. <br><br>The ARGUMENTS section.  This section spells out the arguments that the function takes.  There is one line per argument.  The name of the argument is listed, followed by a dash and a short description of the argument. <br><br>The RETURNS section.  This section describes the value that is returned by the function.  If there is none, place (void) here. <br><br>The NOTES section.  The notes section is optional and does not appear in all function comment headers.  When present, it serves the same purpose as the notes section in the module comment header.  I use this section to leave notes that would be helpful to anyone who has to modify the code months down the road. <br><br>Pf in the comment header is used by an <a href="chapter7.html#doctools">automatic documentation tool</a>. <br><br>Following the comment header is the entry point function itself.  The template for an entry point function looks like the following. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Template for entry point function</b>return-type APIENTRY FunctionName( type arg1, ..., type argN ){    (function body, usually in fault-tolerant form)} /* FunctionName */</pre></tr></td></table> <br>Notice that the new-style standard C form of declaring the argument list is used.  Also, following the ending brace of the function is a comment containing the name of the function the end brace belongs to.  The only thing remaining is the usage of APIENTRY. <br><br>APIENTRY is a macro that is used to assign attributes to entry point functions.  The important thing to remember is that APIENTRY is used to present a logical view to the programmer.  The actual implementation of APIENTRY varies from environment to environment. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>The APIENTRY define</b>#define APIENTRY FAR PASCAL</pre></tr></td></table> <br>For example, if you are programming under the Intel segmented architecture and using Microsoft's C8 compiler, FAR and PASCAL are actually defined to be something (see  <a href="chapter3.html#segmentedflat">&sect;3.2.1</a>). Due to the segmented architecture and the possibility of near or far code, APIENTRY functions must be accessible to other modules and hence, declared as FAR.  Using PASCAL is optional but provides a savings in code size due to how arguments are pushed and popped (see  <a href="chapter2.html#pascalcalling">&sect;2.1.11</a>). <br><br>If you are programming in a 32-bit flat model environment, FAR and PASCAL are defined to be nothing, so the function is public and accessible to other modules, which is the desired behavior. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>The APIENTRY define, specific to Microsoft C8 Windows DLL programming</b>#define APIENTRY EXPORT FAR PASCAL</pre></tr></td></table> <br>Finally, if you are programming under Microsoft Windows and writing a DLL, you want to ensure that your API functions are exported.  This is done with the EXPORT keyword. <br><br>APIENTRY is specifying what to do, not how to do it.  The how is left to a macro that can be easily changed at any time.  This also allows for a single code base that can be targeted to multiple platforms without any code changes. <br><br><a name="localfunctions"></a><b>6.6.7 LOCAL Functions</b> <br><br>In the process of implementing modules I believe you will quickly find out that it is not always possible or desirable to fully implement an APIENTRY function in one function.  You will end up calling support or helper functions that are private to the module you are working on. <br><br>

⌨️ 快捷键说明

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