Chapter 6: Structures
A structure is a collection of one or more variables, possibly of different types, grouped together under a single name for convenient handling. Structures help to organize complicated data, particularly in large programs, because they permit a group of related variables to be treated as a unit instead of as separate entities.
6.1 Basics of Structures
The keyword struct introduces a structure declaration, which is a list of declarations enclosed in braces. An optional name called a structure tag may follow the word struct. The tag names this kind of structure, and can be used subsequently as a shorthand for the part of the declaration in braces. The variables named in a structure are called members. A struct declaration defines a type.
A structure member or tag and an ordinary (i.e, non-member) variable can have the same name without conflict. The same member names may occur in different structure.
A structure declaration that is not followed by a list of variables reserves no storage; it merely describes a template or the shape of a structure.
A structure can be initialized by following its definition with a list of initializers.
A member of a particular structure is referred to in an expression by a construction of the form structure-name.member.
Structures can be nested.
6.2 Structures and Functions
The only legal operations on a structure are copying it or assigning to it as a unit, take its address with &, and accessing its members.
If a large structure is to be passed to a function, it is generally more efficient to pass a pointer than to copy the whole structure.
The parentheses are necessary in (*pp.x) because the precedence of the structure member operator . is higher than *.
If p is a pointer to a structure, then p->member-of-structure refers to the particular member.
The structure operators . and ->, together with () for function calls and [] for subscripts, are at the top of the precedence hierarchy and thus bind very tighly.
6.3 Arrays of Structure
struct key { char *word; int count; } keytab[NKEYS];
The structure declaration declares a structure type key, defines an array keytab of structures of this type, and sets aside storage for them. Each element of the array is a structure.
The size of the array is the size of one entry times the number of entries, so the number of entries is just sizeof keytab / sizeof(struct key) or sizeof keytab / sizeof keytab[0]. C provides a compile-time unary operator called sizeof that can be used to compute the size of any object. The expressions sizeof object and sizeof(type name) yield an integer equal to the size of the specified object or type in bytes. (Strictly, sizeof produces an unsigned integer value whose type, size_t, is defined in the header <stddef.h>). An object can be a variable or array or structure. A type name can be the name of a basic type like int or double, or a derived type like a structure or a pointer.
A sizeof can not be used in a #if line, because the preprocesser does not parse type names. But the expression in the #define is not evaluated by the preprocesser, so the code here is legal.
6.4 Pointers to Structure
If p is a pointer to a structure, arithmetic on p takes into account the size of the structure, so p++ increments p by the correct amount to get the next element of the array of structures.
Because of alignment requirements for different objects, there may be unnamed "holes" in a structure. Thus, for instance, if a char is one byte and an int is four bytes, the structure
struct { char c; int i; };
might well require eight bytes, not five. The sizeof operator returns the proper value.
6.5 Self-referential Structures
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 */ };
This recursive declaration of a node might look chancy, but it is correct. It is illegal for a structure to contain an instance of itself, but struct tnode *left; declares left to be a pointer to a tnode, not a tnode itself.
6.6 Table Lookup
Hash search---the incoming name is converted into a small non-negative integer, which is then used to index into an array of pointers. An array element points to the begining of a linked list of blocks describing names that have that hash value. It is NULL if no names have hashed to that value.
6.7 Typedef
C provides a facility called typedef for creating new data type names.
typedef struct tnode *Treeptr; typedef struct tnode { /* the tree node: */ char *word; /* points to the next */ int count; /* number of occurrences */ Treeptr left; /* left child */ Treeptr right; /* right child */ } Treenode;
This creates two new type keywords called Treenode (a structure) and Treeptr (a pointer to the structure).
It must be emphasized that a typedef declaration does not create a new type in any sense; it merely adds a new name for some existing type.
6.8 Unions
A union is a variable that may hold (at different times) objects of different types and sizes, with the complier keeping track of size and alignment requirements.
union u_tag { int ival; float fval; char *sval; } u;
The variable u will be large enough to hold the largest of the three types; the specific size is implementation-dependent. Any one of these types may be assigned to u and then used in expressions, so long as the usage is consistent: the type retrieved must be the type most recently stored. It is the programer's responsibility to keep track of which type is currently stored in a union; the results are implementation-dependent if something is stored as one type and extracted as another.
Members of a union are accessed as union-name.member or union-pointer->member.
In effect, a union is a structure in which all members have offset zero from the base, the structure is big enough to hold the "widest" member and the alignment is appropriate for all of the types in the union. The same operations are permitted on unions as on structures: assignment to or copying as a unit, taking the address, and accessing a member.
A union may only be initialized with a value of the type of its first member; thus the union u described above can only be initialized with an integer value.
6.9 Bit-fields
When storage space is at a premium, it may be necessary to pack several objects into a single machine word; one common use is a set of single-bit flags in applications like complier symbol tables.
The most compact way to encode such infromation is a set of one-bit flags in a single char or int. The usual way this is done is to define a set of "masks" corresponding to the relevant bit positions, as in
#define KEYWORD 01 #define EXTERNAL 02 #define STATIC 04
The numbers must be powers of two. Then accessing the bits becomes a matter of "bit-fiddling" with the shifting, masking, and complementing operators that were described in Chapter 2.
Another way. A bit-field or field for short, is a set of adjacent bits within a single implementation-defined storage unit that we will call a "word". The syntax of field definition and access is based on structures. For example,
struct { unsigned int is_keyword : 1; unsigned int is_extern : 1; unsigned int is_static : 1; } flags;
This defines a variable called flags that contains three 1-bit fields. The number following the colon represents the field width in bits. The fields are declared unsigned int to ensure that they are unsigned quantities.
Almost everything about fields is implementation-dependent. Fields are assigned left to right on some machines and right to left on others.

浙公网安备 33010602011771号