📄 chapter2.html
字号:
The step-by-step rules for reading most valid variable declarations are as follows:<ul type="disc"><li>Find the variable name in the declaration. Say "variable name is a" and try to move to the right.<li>To try to move to the right:<ol type="a"> <li>If in an empty set of parentheses, discard the parentheses and try to move to the right. <li>If you find a right parentheses, try to move to the left. <li>If you find [count], say "array of count" and try to move to the right. <li>If you find (args), say "function taking args and returning a" and try to move to the right. <li>If you find a semicolon, try to move to the left. </ol><li>To try to move to the left:<ol type="a"> <li>If you find an asterisk, say "pointer to a" and try to move to the right. <li>If you find a data type, say "data type" and try to move to the right. </ol> </ul> <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Sample variable declaration one</b>char (*buffer)[80]; ^ ^ ^ ^ 4 2 1 3</pre></tr></td></table><br>1. buffer is a<br>2. pointer to an<br>3. array of 80<br>4. char<br><br>In English, the translation goes like this. You start with char (*buffer)[80] with the caret ^ indicating your position. Find the variable name buffer and say (1) buffer is a. You now are left with char (*^)[80]. You move to the right and find a right parenthesis, so you move to the left, find an asterisk and say (2) pointer to an. You are now left with char (^)[80]. You move to the right and find that you are in an empty set of parentheses so you now discard them. You are now left with char ^[80]. You move to the right and find an array, so you say (3) array of 80 and try to move to the left. You are now left with char, a data type, so you say (4) char. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Sample variable declaration two</b>int (*(*testing)(int))[10]; ^ ^ ^ ^ ^ ^ 6 4 2 1 3 5</pre></tr></td></table><br>1. testing is a<br>2. pointer to a<br>3. function taking an integer and returning a<br>4. pointer to an<br>5. array of ten<br>6. integers<br> <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Sample variable declaration three: signal function</b>void (*signal(int, void (*)(int)))(int); ^ ^ ^ ^ ^ ^ ^ ^ 8 6 1 2 5 3 4 7</pre></tr></td></table><br>1. signal is a<br>2. function taking an integer for the first argument and a<br>3. pointer to a<br>4. function taking an integer and returning a<br>5. void for the second argument. This signal function returns a<br>6. pointer to a<br>7. function taking an integer and returning a<br>8. void<br> <br><br>Going back to the question at the beginning of this section, do you know if there is a difference between char buffer[80] and char (*buffer)[80]? What is buffer? Does it look as if buffer is a pointer to an array of 80 characters in both cases? <br><br>In the first case, buffer is an array of 80 characters. In the second case, buffer is a pointer to an array of 80 characters. It may help clarify the problem if you ask "What is the data type of *buffer?" in both cases. In the first case, it is a character. In the second case, it is an array of 80 characters. So there is a seemingly slight but quite significant difference. <br><br>This is just an introduction to variable declarations. I hope that you have a new insight into how to read declarations and urge you to look into the topic further. <br><br><b>2.1.6 Typedef's Made Easy</b> <br><br>I can remember when I first started to learn C that I had difficulty with typedef's. I do not know exactly why, but one day it just hit me. Take any valid variable definition, add typedef to the front of it and the variable name is now the data type. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>PSTR is a variable that is a character pointer</b>char *PSTR;</pre></tr></td></table> <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>PSTR is a new type that is a character pointer</b>typedef char *PSTR;</pre></tr></td></table> <br><br><b>2.1.7 Name Space</b> <br><br>It is possible in C and C++ to spell a typedef name and a variable name exactly the same. Consider the following. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Code that works, but is a bad programming practice</b>typedef int x;int main(void){ x x; return 0;} /* main */</pre></tr></td></table> <br>While some people may consider this neat, it leads to programs that can be hard to read and understand. My advice to you is to assume that everything has a unique name. <blockquote><table bgcolor="#E0E0E0" border=1 cellpadding=2 cellspacing=0><tr><td> Keep all the names in your programs unique. </tr></td></table></blockquote>If you follow the programming methodologies described in later chapters, you will never have the possibility of name collisions. <br><br><a name="cschar"></a><table bgcolor="#F0F0F0"><tr><td><img src="./windows.gif"> <b>2.1.8 Code Segment Variables</b> <br><br>An interesting feature of the Microsoft C8 compiler is the ability to place read-only variables in the code segment. Read/Write variables are not permitted since writing to a code segment is an access violation, resulting in a general protection fault. A set of macros that help to place read-only strings in the code segment are as follows. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>CSCHAR define</b>#define BASEDIN(seg) _based(_segname(#seg))#define CSCHAR static char BASEDIN(_CODE)</pre></tr></td></table> <br>CSCHAR (code segment char) may be used just as a char would be used. Notice the usage of static in the #define. This indicates that the variable is allocated permanent storage and that it is known only in the scope in which it is defined. <br><br>CSCHAR uses based pointers to place a string in the code segment. Based pointers are not discussed here since there is an excellent discussion of based pointers in the Microsoft C documentation and in the September 1990 <a href="references.html">Microsoft Systems Journal article</a> by Richard Shaw. <blockquote><table bgcolor="#E0E0E0" border=1 cellpadding=2 cellspacing=0><tr><td> CSCHAR is a great way of placing read-only strings in the code segment. </tr></td></table></blockquote>For non-segmented architectures, #define the BASEDIN() macro to be nothing. While this does not give you a code segment variable, it does allow you to port the code easily. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Using CSCHAR</b>CSCHAR szFile[]=__FILE__;...OutputName(szFile);</pre></tr></td></table> <br>Another use of code segment variables is for placing read-only data tables in the segment in which they are used. One ideal application of this is in the generation of 16-bit and 32-bit cyclic redundancy checks (CRCs). There are well known table-driven methods for speeding up CRC calculations. If these tables were contained in a data segment, they would use valuable space and would always be in memory. By embedding the tables in the code segment that does the CRC calculations, you free up data segment storage. The code segment is more than likely marked as discardable under Windows; therefore you now have code and read-only data that is read in from the disk and discarded as a set. </td></tr></table> <br><b>2.1.9 Adjacent String Literals</b> <br><br>Consider the following sample code and the output that is produced. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Sample code</b>#include <stdio.h>int main(void){ printf( "String: %s" "One","Two" "Three" ); return 0;} /* main */<br><b>Output from the sample code</b>String: TwoThreeOne</pre></tr></td></table> <br>Notice how adjacent string literals are being concatenated in this sample code. The first concatenation is between "String: %s" and "One" to yield "String: %sOne". The second concatenation is between "Two" and "Three" to yield "TwoThree". So the statement really is printf( "String: %sOne", "TwoThree" );, which explains the output produced by this code. <br><br>In Microsoft C8, string concatenation is done by the compiler, not by the preprocessor. This feature is especially useful for splitting long strings across multiple source lines as in the following example. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Splitting up long strings</b>#include <stdio.h>int main(void){ printf( "This is an example of using C's ability to\n" "contatenate adjacent string literals. It\n" "is a great way to get ONE printf to display\n" "a help message.\n" ); return 0;} /* main */</pre></tr></td></table> <br>It is also a good way to place macro arguments in a string. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Placing macro arguments in a string literal</b>#define PRINTD(var) printf( "Variable " #var "=%d\n", var )</pre></tr></td></table><br>PRINTD() works by using the <a href="#stringizing">stringizing operator (#)</a> on the macro argument, namely #var. This places the macro argument in quotes and allows the compiler to concatenate the adjacent string literals. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>PRINTD() example</b>#include <stdio.h>#define PRINTD(var) printf( "Variable " #var "=%d\n", var )int main(void){ int nTest=10; PRINTD(nTest); return 0;} /* main */<br><b>PRINTD() example output</b>Variable nTest=10</pre></tr></td></table><br><table bgcolor="#F0F0F0"><tr><td><img src="./windows.gif"> <b>2.1.10 Variable Number of Arguments</b> <br><br>The following discussion is specific to Microsoft C8 and the Intel machine architecture. Other compilers and machine architectures may implement function calls and argument passing differently. <br><br>C is one of the few languages where passing a variable number of arguments to a function is incredibly easy and incredibly powerful. This mechanism is what is used by printf(). <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Printf() prototype for Microsoft C8 in stdio.h</b>int __cdecl printf(const char *, ...);</pre></tr></td></table> <br>The ... declaration indicates to the compiler that the following types and number of arguments may vary. It can appear only at the end of an argument list. <br><br>Pretend for the moment that you are the compiler writer. How would you implement a variable number of arguments being passed to a function? <br><br>Traditional architecture. Consider for a moment what happens at the machine level when a function call is made. A caller pushes onto the stack the arguments that are required by a function and then the function call is made, which pushes the return address onto the stack. You are now executing the function. This function knows that there is a return address on the stack and the number, type and relative stack position of each argument. When the function is ready to return to the caller, it obtains the return address from the stack, removes the arguments from the stack and returns to the caller. <br><br>Let us now consider printf(). With a variable number of arguments, how does the printf() function know how many arguments to pop off the stack when returning from the function? It cannot. The solution is to let the caller restore any arguments pushed onto the stack. <br><br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -