📄 ch05.htm
字号:
letter occurs in an array of characters. On line 2 the counter is initialized to zero. On line 3 we begin a <tt>for</tt> loop that iterates through every <tt>position</tt> in the array.</p><p>On line 5 we test each character to see whether it matches the character that was sent in to be tested; if so, we increment the counter. Note that the braces at lines 4 and 7 are not technically necessary, but as Donald Xie pointed out when editing this book, they do make the code much easier to read. </p><p>Finally, on line 8 we return that value.</p><p>In Listing 5.9, on line 7, we now have a value on the right side of the assignment that represents how many times <tt>alpha[i]</tt> occurs in <tt>thisGuess</tt>: that is, in the array that is passed in from <tt>Play()</tt>.</p><p>On line 8, we compute the same value for the solution. The value of <tt>correct</tt> is the lesser of these two, which we accomplish on lines 9 and 10 by using the ternary operator to find the smaller value.</p><p>An example makes this clearer: If the solution has <i>aabba</i> and the guess has <i>ababc</i>, we examine the first letter <i>a</i>. <tt>howMany() </tt>returns <tt>2</tt> for the guess and <tt>3</tt> for the solution, so the player has the lesser, <tt>2</tt>, correct.</p><p>On lines 14-18, we iterate again through the loops, this time testing on line 16 to see whether the character at a specific offset in <tt>thisGuess</tt> is the same as the character at the same offset in the solution. If so, another letter is in the right position.</p><p>Because <tt>correct</tt> and <tt>position</tt> were passed in as references, the changes that are made in <tt>Score() </tt>are reflected back in <tt>Play()</tt>. <a name="_Toc444312813"></a></p><h2> <a name="Heading15">Using ASSERT</a></h2><p>Before moving on, I want to demonstrate how this code can be made both more reliable and more understandable through the use of <tt>ASSERT</tt>.</p><p>The purpose of <tt>ASSERT</tt> is to test your assumptions when you are debugging your code, but to have no effect at all when you release your final production version.</p><p>When you are debugging your code, you signal your compiler to enter <i>debug mode</i>. When you are ready to release your program to the paying public, you rebuild in <i>release mode</i>. Debug mode brings along a lot of debugging information that you don't want in release mode. </p><p>Thus, in debug mode, you can write</p><pre><tt>ASSERT ( <tt>position</tt> <= <tt>correct</tt> )</tt></pre><p>Here you are simultaneously documenting your belief that <tt>position</tt> must never be larger than <tt>correct</tt>. (You can never have five in the correct position if you only have four correct letters!) You are also testing that assertion each time the code runs to prove that you are right. In debug mode, if <tt>position</tt> ever is<b> </b>larger than <tt>correct</tt>, this <tt>ASSERT</tt> statement fails and an error message is written. </p><p>When your program is ready to be released, the <tt>ASSERT</tt> macro magically disappears and has no effect on the efficiency of your code.</p><h3> <a name="Heading16">How ASSERT Works</a></h3><p><tt>ASSERT</tt> is typically implemented as a <i>macro</i>. Macros are left over from C; they are type-unsafe routines that are processed not by the compiler but by the precompiler, the same beast that handles your <tt>#include</tt> and <tt>#define</tt> statements. In fact, a macro <i>is</i> a <tt>#define</tt> statement.</p><blockquote> <hr> <p><b>macro</b>--A text substitution by the precompiler. Macros can act as small subprograms.</p> <hr></blockquote><h2> <a name="Heading17">Excursion: Macros</a></h2><p>A macro function is a symbol that is created using <tt>#define</tt>, which takes an argument much like a function does, and which replaces the macro and its argument with a <i>substitution string</i>. For example, you can define the macro <tt>TWICE</tt> as follows:</p><pre><tt>#define TWICE(x) ( (x) * 2 )</tt></pre><p>Then in your code you write</p><pre><tt>TWICE(4)</tt></pre><p>The entire string <tt>TWICE(4)</tt> is removed and the value <tt>4*2</tt> is substituted. When the precompiler sees TWICE(<tt>4)</tt>, it substitutes <tt>( (4) * 2 )</tt>. That is just what you want because 4*2 evaluates to 8, so <tt>TWICE</tt> will have done just the work you expected.</p><p>A macro can have more than one parameter, and each parameter can be used repeatedly in the replacement text. Two common macros are <tt>MAX</tt> and <tt>MIN</tt>:</p><pre><tt>#define MAX(x,y) ( (x) > (y) ? (x) : (y) )</tt><tt>#define MIN(x,y) ( (x) < (y) ? (x) : (y) )</tt></pre><p><tt>MAX</tt> returns the larger of two values (<tt>x</tt> and <tt>y</tt>), and <tt>MIN</tt> returns the lesser. Thus, <tt>MAX(7,5)</tt> is <tt>7</tt>, and <tt>MIN(7,5)</tt> is <tt>5</tt>.</p><blockquote> <hr> <p><strong>NOTE: </strong> In a macro function definition, the opening parenthesis for the parameter list must immediately follow the macro name, with no spaces. The preprocessor is not as forgiving of white space as is the compiler. <a name="_Toc382902686"></a><a name="_Toc444312816"></a></p> <hr></blockquote><h3> <a name="Heading18">Why All the Parentheses?</a></h3><p>You might be wondering why there are so many parentheses in these macros. The preprocessor does not demand that parentheses be placed around the arguments in the substitution string, but the parentheses help you avoid unwanted side effects when you pass complicated values to a macro. For example, if you define <tt>MAX</tt> as</p><pre><tt>#define MAX(x,y) x > y ? x : y</tt></pre><p>and pass in the values <tt>5</tt> and <tt>7</tt>, the macro works as intended. If you pass in a more complicated expression, however, you'll get unintended results, as shown in Listing 5.11.</p><h4> Listing 5.11 Unintended Macro Results</h4><pre><tt>0: </tt><tt>1: #include <iostream.h></tt><tt>2: </tt><tt>3: #define CUBE(a) ( (a) * (a) * (a) )</tt><tt>4: #define THREE(a) a * a * a</tt><tt>5: </tt><tt>6: int main()</tt><tt>7: {</tt><tt>8: long x = 5;</tt><tt>9: long y = CUBE(x);</tt><tt>10: long z = THREE(x);</tt><tt>11: </tt><tt>12: cout << "y: " << y << endl;</tt><tt>13: cout << "z: " << z << endl;</tt><tt>14: </tt><tt>15: long a = 5, b = 7;</tt><tt>16: y = CUBE(a+b);</tt><tt>17: z = THREE(a+b);</tt><tt>18: </tt><tt>19: cout << "y: " << y << endl;</tt><tt>20: cout << "z: " << z << endl;</tt><tt>21: return 0;</tt><tt>22: }</tt><tt>***Please Insert Output icon herey: 125</tt><tt>z: 125</tt><tt>y: 1728</tt><tt>z: 82</tt></pre><p>On line 1, we use the old-fashioned iostream.h so that we can avoid using namespaces. This is perfectly legal in C++, and it is common in writing very short demonstration programs.</p><p>On line 3, the macro <tt>CUBE</tt> is defined, with the argument <tt>x</tt> put into parentheses each time it is used. On line 4, the macro <tt>THREE</tt> is defined, without the parentheses. It is intended for these macros to do exactly the same thing: to multiply their arguments times themselves, three times.</p><p>In the first use of these macros, on line 16, the value <tt>5</tt> is given as the parameter and both macros work fine. <tt>CUBE(5)</tt> expands to <tt>( (5) * (5) * (5) )</tt>, which evaluates to <tt>125</tt>, and <tt>THREE(5)</tt> expands to <tt>5 * 5 * 5</tt>, which also evaluates to <tt>125</tt>.</p><p>In the second use, on line 17, the parameter is <tt>5 + 7</tt>. In this case, <tt>CUBE(5+7)</tt> evaluates to</p><pre><tt> ( (5+7) * (5+7) * (5+7) )</tt></pre><p>which evaluates to</p><pre><tt> ( (12) * (12) * (12) )</tt></pre><p>which in turn evaluates to <tt>1,728</tt>. <tt>THREE(5+7)</tt>, however, evaluates to</p><pre><tt>5 + 7 * 5 + 7 * 5 + 7</tt></pre><p>Because multiplication has a higher precedence than addition, this becomes</p><pre><tt>5 + (7 * 5) + (7 * 5) + 7</tt></pre><p>which evaluates to</p><pre><tt>5 + (35) + (35) + 7</tt></pre><p>which finally evaluates to <tt>82</tt>. <a name="_Toc382902687"></a><a name="_Toc444312817"></a></p><h3> <a name="Heading19">Macros Versus Functions</a></h3><p>Macros suffer from four problems in the eyes of a C++ programmer. First, because all macros must be defined on one line, they can be confusing if they become large. You can extend that line by using the backslash character (<tt>\</tt>), but large macros quickly become difficult to manage.</p><p>Second, macros are expanded inline each time they are used. This means that if a macro is used a dozen times, the substitution appears 12 times in your program, rather than appearing once as a function call does. On the other hand, they are usually quicker than a function call because the overhead of a function call is avoided.</p><p>The fact that they are expanded inline leads to the third problem, which is that the macro does not appear in the intermediate source code that is used by the compiler, and therefore it is not visible in most debuggers. By the time you see it in the debugger, the substitution is already accomplished. This makes debugging macros tricky.</p><p>The final problem, however, is the largest: Macros are not type-safe. Although it is convenient that absolutely any argument can be used with a macro, this completely undermines the strong typing of C++ and so is anathema to C++ programmers. </p><p>That said, the <tt>ASSERT</tt> macro is a good example of a time when this is not a bug, but a feature: One <tt>ASSERT</tt> macro can test any condition, mathematical or otherwise. <a name="_Toc382902689"></a><a name="_Toc444312818"></a></p><h2> <a name="Heading20">String Manipulation</a></h2><p>The preprocessor provides two special operators for manipulating strings in macros. The <i>stringizing operator</i> (<tt>#</tt>) substitutes a quoted string for whatever follows the stringizing operator. The <i>concatenation operator</i> (<tt>##</tt>) bonds two strings together into one.</p><blockquote> <hr> <p><strong>NOTE: </strong> The <i>stringizing operator</i> (<tt>#</tt>) substitutes a quoted string for whatever follows the stringizing operator.</p> <p> The <i>concatenation operator</i> (<tt>##</tt>) bonds two strings together into one. <a name="_Toc382902690"></a><a name="_Toc444312819"></a></p> <hr></blockquote><h3> <a name="Heading21">Stringizing</a></h3><p>The stringizing operator(<tt>#</tt>) puts quotes around any characters that follow the operator, up to the next white space. Thus, if you write</p><pre><tt>#define WRITESTRING(x) cout << #x</tt></pre><p>and then call</p><pre><tt>WRITESTRING(This is a string);</tt></pre><p>the precompiler turns it into</p><pre><tt>cout << "This is a string";</tt></pre><p>Note that the string <tt>This is a string</tt> is put into quotes, as is required by <tt>cout</tt>. <a name="_Toc382902691"></a><a name="_Toc444312820"></a></p><h3> <a name="Heading22">Concatenation</a></h3><p>The concatenation operator (<tt>##</tt>) enables you to bond together more than one term into a new word. The new word is actually a token that can be used as a class name, a variable name, or an offset into an array--or anywhere else a series of letters might appear.</p><p>Assume for a moment that you have five functions named <tt>fOnePrint</tt>, <tt>fTwoPrint</tt>, <tt>fThreePrint</tt>, <tt>fFourPrint</tt>, and <tt>fFivePrint</tt>. You can then declare</p><pre><tt>#define fPRINT(x) f ## x ## Print</tt></pre><p>and then use it with <tt>fPRINT(Two)</tt> to generate <tt>fTwoPrint</tt>, and with <tt>fPRINT(Three)</tt> to generate <tt>fThreePrint</tt>. <a name="_Toc382902692"></a><a name="_Toc444312821"></a></p><h2> <a name="Heading23">Predefined Macros</a></h2><p>Many compilers predefine a number of useful macros, including <tt>__DATE__</tt>, <tt>__TIME__</tt>, <tt>__LINE__</tt>, and <tt>__FILE__</tt>. Each of these names is surrounded by two underscore<tt> </tt>characters to reduce the likelihood that the names will conflict with names you've used in your program.</p><p>When the precompiler sees one of these macros, it makes the appropriate substitutes. For <tt>__DATE__</tt>, the current Date is substituted; for <tt>__TIME__</tt>, the current time is substituted. <tt>__LINE__</tt> and <tt>__FILE__</tt> are replaced with the source code line number and filename, respectively. Note that this substitution is made when the source is precompiled, not when the program is run. If you ask the program to print <tt>__DATE__</tt>, you do not get the current date; instead, you get the date the program was compiled. These defined macros are very useful in debugging.</p><p>Although many compilers do provide an <tt>ASSERT</tt> macro, it will be instructive to create our own, shown in Listing 5.12.</p><h4> Listing 5.12 An ASSERT Macro</h4><pre><tt>0: #define DEBUG</tt><tt>1: </tt><tt>2: #ifndef DEBUG</tt><tt>3: #define ASSERT(x)</tt><tt>4: #else</tt><tt>5: #define ASSERT(x) \</tt><tt>6: if (! (x)) \</tt><tt>7: { \</tt><tt>8: cout << "ER
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -