📄 chapter6.html
字号:
<tt>install</tt> uses <tt>lookup</tt> to determine whether the name beinginstalled is already present; if so, the new definition will supersede theold one. Otherwise, a new entry is created. <tt>install</tt> returns<tt>NULL</tt> if for any reason there is no room for a new entry.<pre> struct nlist *lookup(char *); char *strdup(char *); /* install: put (name, defn) in hashtab */ struct nlist *install(char *name, char *defn) { struct nlist *np; unsigned hashval; if ((np = lookup(name)) == NULL) { /* not found */ np = (struct nlist *) malloc(sizeof(*np)); if (np == NULL || (np->name = strdup(name)) == NULL) return NULL; hashval = hash(name); np->next = hashtab[hashval]; hashtab[hashval] = np; } else /* already there */ free((void *) np->defn); /*free previous defn */ if ((np->defn = strdup(defn)) == NULL) return NULL; return np; }</pre><strong>Exercise 6-5.</strong> Write a function <tt>undef</tt> that will remove a name anddefinition from the table maintained by <tt>lookup</tt> and <tt>install</tt>.<p><strong>Exercise 6-6.</strong> Implement a simple version of the <tt>#define</tt> processor(i.e., no arguments) suitable for use with C programs, based on the routines ofthis section. You may also find <tt>getch</tt> and <tt>ungetch</tt> helpful.<h2><a name="s6.7">6.7 Typedef</a></h2>C provides a facility called <tt>typedef</tt> for creating new data type names.For example, the declaration<pre> typedef int Length;</pre>makes the name <tt>Length</tt> a synonym for <tt>int</tt>. The type<tt>Length</tt> can be used in declarations, casts, etc., in exactly the sameways that the <tt>int</tt> type can be:<pre> Length len, maxlen; Length *lengths[];</pre>Similarly, the declaration<pre> typedef char *String;</pre>makes <tt>String</tt> a synonym for <tt>char *</tt> or character pointer,which may then be used in declarations and casts:<pre> String p, lineptr[MAXLINES], alloc(int); int strcmp(String, String); p = (String) malloc(100);</pre>Notice that the type being declared in a <tt>typedef</tt> appears in theposition of a variable name, not right after the word <tt>typedef</tt>.Syntactically, <tt>typedef</tt> is like the storage classes <tt>extern</tt>,<tt>static</tt>, etc. We have used capitalized names for <tt>typedef</tt>s,to make them stand out.<p>As a more complicated example, we could make <tt>typedef</tt>s for the tree nodesshown earlier in this chapter:<pre> typedef struct tnode *Treeptr; typedef struct tnode { /* the tree node: */ char *word; /* points to the text */ int count; /* number of occurrences */ struct tnode *left; /* left child */ struct tnode *right; /* right child */ } Treenode;</pre>This creates two new type keywords called <tt>Treenode</tt> (a structure) and<tt>Treeptr</tt> (a pointer to the structure). Then the routine <tt>talloc</tt>could become<pre> Treeptr talloc(void) { return (Treeptr) malloc(sizeof(Treenode)); }</pre>It must be emphasized that a <tt>typedef</tt> declaration does not create a newtype in any sense; it merely adds a new name for some existing type. Nor arethere any new semantics: variables declared this way have exactly the sameproperties as variables whose declarations are spelled out explicitly. Ineffect, <tt>typedef</tt> is like <tt>#define</tt>, except that since it isinterpreted by the compiler, it can cope with textual substitutions that arebeyond the capabilities of the preprocessor. For example,<pre> typedef int (*PFI)(char *, char *);</pre>creates the type <tt>PFI</tt>, for ``pointer to function (of two <tt>char *</tt>arguments) returning <tt>int</tt>,'' which can be used in contexts like<pre> PFI strcmp, numcmp;</pre>in the sort program of <a href="chapter5.html">Chapter 5</a>.<p>Besides purely aesthetic issues, there are two main reasons for using<tt>typedef</tt>s. The first is to parameterize a program against portabilityproblems. If <tt>typedef</tt>s are used for data types that may bemachine-dependent, only the <tt>typedef</tt>s need change when the program ismoved. One common situation is to use <tt>typedef</tt> names for various integerquantities, then make an appropriate set of choices of <tt>short</tt>, <tt>int</tt>,and <tt>long</tt> for each host machine. Types like <tt>size_t</tt> and<tt>ptrdiff_t</tt> from the standard library are examples.<p>The second purpose of <tt>typedef</tt>s is to provide better documentation for aprogram - a type called <tt>Treeptr</tt> may be easier to understand than onedeclared only as a pointer to a complicated structure.<h2><a name="s6.8">6.8 Unions</a></h2>A <em>union</em> is a variable that may hold (at different times) objects ofdifferent types and sizes, with the compiler keeping track of size andalignment requirements. Unions provide a way to manipulate different kinds ofdata in a single area of storage, without embedding any machine-dependentinformation in the program. They are analogous to variant records in pascal.<p>As an example such as might be found in a compiler symbol table manager,suppose that a constant may be an <tt>int</tt>, a <tt>float</tt>, or a characterpointer. The value of a particular constant must be stored in a variable ofthe proper type, yet it is most convenient for table management if the valueoccupies the same amount of storage and is stored in the same placeregardless of its type. This is the purpose of a union - a single variablethat can legitimately hold any of one of several types. The syntax is basedon structures:<pre> union u_tag { int ival; float fval; char *sval; } u;</pre>The variable <tt>u</tt> will be large enough to hold the largest of the threetypes; the specific size is implementation-dependent. Any of these types maybe assigned to <tt>u</tt> and then used in expressions, so long as the usage isconsistent: the type retrieved must be the type most recently stored. It isthe programmer's responsibility to keep track of which type is currentlystored in a union; the results are implementation-dependent if something isstored as one type and extracted as another.<p>Syntactically, members of a union are accessed as<p> <em>union-name</em><tt>.</tt><em>member</em><p>or<p> <em>union-pointer</em><tt>-></tt><em>member</em><p>just as for structures. If the variable <tt>utype</tt> is used to keep trackof the current type stored in <tt>u</tt>, then one might see code such as<pre> if (utype == INT) printf("%d\n", u.ival); if (utype == FLOAT) printf("%f\n", u.fval); if (utype == STRING) printf("%s\n", u.sval); else printf("bad type %d in utype\n", utype);</pre>Unions may occur within structures and arrays, and vice versa. The notationfor accessing a member of a union in a structure (or vice versa) is identicalto that for nested structures. For example, in the structure array defined by<pre> struct { char *name; int flags; int utype; union { int ival; float fval; char *sval; } u; } symtab[NSYM];</pre>the member <tt>ival</tt> is referred to as<pre> symtab[i].u.ival</pre>and the first character of the string <tt>sval</tt> by either of<pre> *symtab[i].u.sval symtab[i].u.sval[0]</pre>In effect, a union is a structure in which all members have offset zero fromthe base, the structure is big enough to hold the ``widest'' member, and thealignment is appropriate for all of the types in the union. The sameoperations are permitted on unions as on structures: assignment to or copyingas a unit, taking the address, and accessing a member.<p>A union may only be initialized with a value of the type of its first member;thus union <tt>u</tt> described above can only be initialized with an integervalue.<p>The storage allocator in <a href="chapter8.html">Chapter 8</a> shows how aunion can be used to force a variable to be aligned on a particular kind ofstorage boundary.<h2><a name="s6.9">6.9 Bit-fields</a></h2>When storage space is at a premium, it may be necessary to pack severalobjects into a single machine word; one common use is a set of single-bitflags in applications like compiler symbol tables. Externally-imposed dataformats, such as interfaces to hardware devices, also often require theability to get at pieces of a word.<p>Imagine a fragment of a compiler that manipulates a symbol table. Eachidentifier in a program has certain information associated with it, forexample, whether or not it is a keyword, whether or not it is external and/orstatic, and so on. The most compact way to encode such information is a setof one-bit flags in a single <tt>char</tt> or <tt>int</tt>.<p>The usual way this is done is to define a set of ``masks'' corresponding tothe relevant bit positions, as in<pre> #define KEYWORD 01 #define EXTRENAL 02 #define STATIC 04</pre>or<pre> enum { KEYWORD = 01, EXTERNAL = 02, STATIC = 04 };</pre>The numbers must be powers of two. Then accessing the bits becomes a matterof ``bit-fiddling'' with the shifting, masking, and complementing operatorsthat were described in <a href="chapter2.html">Chapter 2</a>.<p>Certain idioms appear frequently:<pre> flags |= EXTERNAL | STATIC;</pre>turns on the <tt>EXTERNAL</tt> and <tt>STATIC</tt> bits in <tt>flags</tt>, while<pre> flags &= ~(EXTERNAL | STATIC);</pre>turns them off, and<pre> if ((flags & (EXTERNAL | STATIC)) == 0) ...</pre>is true if both bits are off.<p>Although these idioms are readily mastered, as an alternative C offers thecapability of defining and accessing fields within a word directly ratherthan by bitwise logical operators. A <em>bit-field</em>, or <em>field</em>for short, is a set of adjacent bits within a single implementation-definedstorage unit that we will call a ``word.'' For example, the symbol table<tt>#define</tt>s above could be replaced by the definition of three fields:<pre> struct { unsigned int is_keyword : 1; unsigned int is_extern : 1; unsigned int is_static : 1; } flags;</pre>This defines a variable table called <tt>flags</tt> that contains three 1-bitfields. The number following the colon represents the field width in bits.The fields are declared <tt>unsigned int</tt> to ensure that they are unsignedquantities.<p>Individual fields are referenced in the same way as other structure members:<tt>flags.is_keyword</tt>, <tt>flags.is_extern</tt>, etc. Fields behave likesmall integers, and may participate in arithmetic expressions just like otherintegers. Thus the previous examples may be written more naturally as<pre> flags.is_extern = flags.is_static = 1;</pre>to turn the bits on;<pre> flags.is_extern = flags.is_static = 0;</pre>to turn them off; and<pre> if (flags.is_extern == 0 && flags.is_static == 0) ...</pre>to test them.<p>Almost everything about fields is implementation-dependent. Whether a fieldmay overlap a word boundary is implementation-defined. Fields need not benames; unnamed fields (a colon and width only) are used for padding. Thespecial width 0 may be used to force alignment at the next word boundary.<p>Fields are assigned left to right on some machines and right to left onothers. This means that although fields are useful for maintaininginternally-defined data structures, the question of which end comes firsthas to be carefully considered when picking apart externally-defined data;programs that depend on such things are not portable. Fields may be declaredonly as <tt>int</tt>s; for portability, specify <tt>signed</tt> or<tt>unsigned</tt> explicitly. They are not arrays and they do not haveaddresses, so the <tt>&</tt> operator cannot be applied on them.<p><hr><p align="center"><a href="chapter5.html">Back to Chapter 5</a> -- <a href="kandr.html">Index</a> -- <a href="chapter7.html">Chapter 7</a><p><hr></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -