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

📄 chapter2.html

📁 WRITING BUG-FREE C CODE.
💻 HTML
📖 第 1 页 / 共 5 页
字号:
 desired output of 100. <br><br><a name="stringizing"></a><a name="tokenpasting"></a><b>2.2.7 Preprocessor Operators</b> <br><br>Ask any C programmer what the preprocessor is used for and you hear things like macros, including header files, conditional compilation, and so on.  These are obvious and useful features.  However, ask the same C programmer what preprocessor operators are and you may get a blank stare. <br><br>The two most useful preprocessor operators are the stringizing operator (#) and the token pasting operator (##).  Both of these operators are usually used in the context of a #define directive.  The token pasting operator is at the heart of the class methodology introduced in <a href="chapter4.html">Chapter 4</a>. <br><br>Stringizing operator (#).  This operator causes a macro argument to be enclosed in double quotation marks.  The proper syntax is #operand. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Stringizing operator example</b>#define STRING(x) #x</pre></tr></td></table><br>Therefore, STRING(hello) yields "hello". <br><br>In some older preprocessors, you accomplish stringizing of x in a macro (i.e., #x) directly by enclosing the macro argument x in quotes (i.e., "x").  Try this technique if you do not have a standard C compiler that allows stringizing. <br><br>Token pasting operator (##).  This operator takes two tokens, one on each side of the ## and merges them into one token.  The proper syntax is token1##token2.  This is useful when one or both tokens are expanded macro arguments.  Consider the following example. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Token pasting operator example</b>CSCHAR szPE7008[]="function ptr";CSCHAR szPE700A[]="string ptr";CSCHAR szPE7060[]="coords";...#define EV(n) {0x##n,szPE##n}static struct {    WORD wError;    LPSTR lpError;    } BASEDCS ErrorToTextMap[]={ EV(7008), EV(700A), EV(7060) };</pre></tr></td></table> <br>In this example, EV(7008) expands to {0x7008,szPE7008}, which initializes the wError and lpError elements of ErrorToTextMap (which happens to be in the code segment).  For me, the EV() macro makes this code shorter and easier to understand. <br><br><a name="reiserpasting"></a><b>2.2.8 Token Pasting in a Reiser Preprocessor</b> <br><br>In some old preprocessors that do not support the token pasting operator, it is still possible to perform token pasting provided the preprocessor follows the Reiser model.  This is done by replacing ## with /**/.  This works because the comment /**/ is removed and replaced with nothing, effectively pasting the tokens on either side of /**/. <br><br>This technique does not work in newer standard C compilers that replace comments with a single space character.  However, newer standard C compilers have the token pasting operator, so this technique is not needed. <br><br><b>2.2.9 Preprocessor Commands Containing Preprocessor Commands</b> <br><br>Have you ever wanted to write a macro that referred to another preprocessing directive?  Consider the following example. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Optimize On/Off macros, which do not work</b>#define OPTIMIZEOFF #pragma optimize("",off)#define OPTIMIZEON #pragma optimize("",on)</pre></tr></td></table> <br>The OPTIMIZEOFF and OPTIMIZEON macros attempt to abstract out how optimizations are turned off and turned on during a compilation.  The problem with these macros is that they are trying to perform another preprocessor directive, which is impossible with any standard C preprocessor.  However, this does not mean that it cannot be done. <br><br>The solution to this problem is to run the source through the preprocessor twice during the compile instead of just once.  Most compilers allow you to run only the preprocessing pass of their compiler and redirect the output to a file.  If this output file is then run back through the compiler, the optimize macros work! <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Testing extra preprocessor pass in Microsoft C8 for C code</b>cl -P test.ccl -Tctest.i<br><b>Testing extra preprocessor pass in Microsoft C8 for C++ code</b>cl -P test.cppcl -Tptest.i</pre></tr></td></table><a name="puzzles"><br></a><big><b>2.3 Programming Puzzles</b></big> <br><br>This section is designed to get you thinking.  Some of the puzzles presented here have real programming value, while others have no programming value whatsoever.  The point of these exercises is to get you thinking in a new light about things you already know about.  If you want to skip this section, go to <a href="#windows">&sect;2.4</a> <br><br><a name="ispower2"></a><b>2.3.1 ISPOWER2() Macro</b> <br><br>Try to spend some time coming up with a macro that returns one if the macro argument is a power of two, or returns zero if the macro argument is not a power of two.  The solution is as follows. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Macro that determines if a number is a power of two</b>#define ISPOWER2(x) (!((x)&((x)-1)))</pre></tr></td></table> <br>This ISPOWER2() macro works for any number greater than zero because any number that is a power of two has the binary form "100...0", namely, a binary one followed by any number of binary zeros.  Subtracting one from this number changes the leading binary one to zero and all trailing binary zeros to binary ones.  Therefore, ANDing with the original number produces zero.  Any number that is not a power of two has at least one bit in common with that number minus one.  Therefore ANDing produces a non-zero number.  Since this is just the opposite of the desired return value, the value is NOTed to get the desired result. <br><br>For example, the number 16 in binary is 00010000.  Subtracting 1 from 16 is 15, which in binary is 00001111.  So, ANDing 00010000 (16) with 00001111 (15) yields 00000000 (0) and NOTing gives 1.  This tells us the number 16 is a power of two. <br><br>As another example, the number 100 in binary is 01100100.  Subtracting 1 from 100 is 99, which in binary is 01100011.  So, ANDing 01100100 (100) with 01100011 (99) yields 01100000 (96) and NOTing gives zero.  This tells us that the number 100 is not a power of two. <br><br><b>2.3.2 Integer Math May be Faster</b> <br><br>Let us say you have an integer x and an integer y and you want x to contain 45 percent of the value contained in y.  One obvious solution is to use x = (int)(0.45*y);.  While this works, there is a shortcut that avoids floating pointer math and uses only integer arithmetic.  0.45 is just 45/100 or 9/20.  So why not instead use x = (int)(9L*y/20);?  The only disadvantage of this technique is that you need to watch out for overflows.  Play around with this technique with some small numbers by hand until you get a good feel for what is going on. <br><br><b>2.3.3 Swap Two Variables without a Third Variable</b> <br><br>This is an old assembly language trick that can also be used in C.  Given two assembly language registers, how can you swap the contents of the registers without using a third register or memory location (and, of course, not using a swap instruction if the assembly you are familiar with has such an instruction).  The solution is as follows. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Swapping integers x, y without a third integer using C</b>1.  x ^= y;2.  y ^= x;3.  x ^= y;</pre></tr></td></table> <br>After step 1, x contains (x^y).  After step 2, y contains (y^(x^y)), which is just x.  Finally, after step 3, x contains ((x^y)^x), which is y.  This is it! <br><br>The trick to this technique is realizing that any number XORed with itself is 0.  XOR also has useful applications in GUI environments. <br><br><b>2.3.4 Smoother XORs in a Graphical User Interface</b> <br><br>A standard technique used in Graphical User Interfaces is to invert part of the display screen (using XOR) while the user is resizing a window to show the user the new size of the window.  XORing again to the same screen locations restores the screen image to its original form. <br><br>So, when the user starts to resize the window, the location is marked by XORing the window border.  When the user moves a little bit, the same location is XORed to remove the highlight, the proposed border is sized to the new location and it is XORed onto the screen, showing its new location.  This process works pretty well but it results in screen flicker.  The entire inverted window border is constantly being placed down and removed in its entirety. <br><br>Let's analyze the process in abstract terms using regions.  We have three components:  the screen (SCRN), the old border region (OBR) and the new border region (NBR).  The border is placed down by a SCRN ^= OBR operation.  As the user moves the mouse, the goal is to turn the screen containing the old border region into a screen containing the new border region.  (See Figure 2-3). <blockquote> <img src="images/fig2-3.gif"><br> Figure 2-3: Moving a window border </blockquote>The old way of implementing this is to remove the old border followed by placing down the new border.  The old border is removed by a SCRN ^= OBR operation and the new border is placed down by a SCRN ^= NBR operation.  In other words, SCRN = (SCRN ^ OBR) ^ NBR.  (See Figure 2-4) <blockquote> <img src="images/fig2-4.gif"><br> Figure 2-4: The standard way of moving a window border. </blockquote>Does it really matter in what order you XOR?  No, not at all, since XOR is associative.  So why not instead do SCRN = SCRN ^ (OBR^NBR)?  The screen now updates without even a hint of flicker!  This is because you are finding the difference between the old border region and the new border region (i.e., XOR) and XORing that difference onto the screen.  The result is that the old border is erased and that the new border is now visible.  (See Figure 2-5). <blockquote> <img src="images/fig2-5.gif"><br> Figure 2-5: A better way to move a window border </blockquote>The key to this magic is having region support provided to you by the environment you are working on. <br><br><table bgcolor="#F0F0F0"><tr><td><img src="./windows.gif">For Microsoft Windows, the functions of interest are CreateRectRgn(), SetRectRgn() and CombineRgn().</td></tr></table><br><b>2.3.5 Is ~0 More Portable Than 0xFFFF</b> <br><br>How do you fill an integer variable so that all bits are turned on, but in a portable, data type size independent manner?  Do you use -1?  No, because this assumes a two's complement machine.  What if you are on a one's complement machine?  Do you use 0xFFFF, 0xFFFFFFFF or whatever?  No, since this assumes that there are a particular number of bits in an integer. <br><br>The only thing you can be sure of in every machine is that zero is represented as all bits turned off.  This is the key.  There is a built-in C operator to flip all the bits and it is the one's complement ~ operator.  Therefore, ~0 fills an integer value with all ones in a portable manner. <br><br>This technique also works well to strip off the lower bits of a number in a portable manner.  For example, ~7 has all the bits in the resulting number turned on except for the lower three bits.  Therefore, wSize&~7 forces the lower three bits of wSize to zero. <br><br><b>2.3.6 Does y = -x; Always Work?</b> <br><br>You may be wondering if I wrote that statement correctly.  I did.  Do you know when y = -x fails?  It fails on a two's complement machine when x equals the smallest negative number.  Consider a 16-bit two's complement number where the smallest negative number is -32768.  So, if it fails, what is the result of negative -32768?  It is -32768.  The valid range of numbers on a two's complement machine for a short integer is -32768 to 32767.  There is one more negative number than there are positive numbers. <br><br><b>2.3.7 One's and Two's Complement Numbers</b> <br><br>In one's complement notation, the negative of a number is obtained by inverting all the bits in the number.  For short (16-bit) integers, this results in a range from -32767 to 32767.  So what happened to the extra number?  Well, there are now two representations for zero.  Namely a negative zero (all bits on) and a positive zero (all bits off).  One's complement works, but it requires special case hardware to make sure everything works.  One day someone came up with the bright idea of two's complement, which is simply one's complement plus one.  It eliminates the two representations for zero and eliminates the special case hardware. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Two's complement negation as a macro</b>#define NEGATE(x) (((x)^~0)+1)</pre></tr></td></table> <br>So why can two's complement be implemented so efficiently?  For simplicity, let's assume a three-bit unsigned integer.  The integer can take on the values from 0 to 7.  Any arithmetic on the numbers are modulo 8 (remainder upon division by 8).  As an example, adding 1 to 7 is 0 and adding 3 to 7 is 2.  You can verify this by going to the first number (e.g., 7) on the base eight number line and moving right the number of times specified by the second number (e.g., 3). <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre

⌨️ 快捷键说明

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