Previous Table of Contents Next


3.7.2. Structure Member Access

As mentioned, structure members are accessed by name. The basic means for doing so is the structure member access operator, which is a single dot. Continuing the preceding examples, we can refer to c1.re, c2.im, ll.x, ur.y, and so on. Just as for array and pointer access, these constructions can be used on either side of an assignment operator; that is, they can be used both to fetch and to store structure members.

The dot operator has very high precedence, on the same level as array access and function calls, and higher than that of the pointer indirection operator, *. Therefore, an expression like *s.m would mean to select the member m from the structure s and then fetch the object pointed to. (Naturally, this would work only if s were a structure and the member m had pointer type.) When you’re accessing members in structures pointed to by structure pointers, therefore, a certain amount of care would be necessary. For example, if pc is a pointer to struct complex, using the . operator to select the member re requires explicit parentheses:

(*pc).re = 3;

Because pointers to structures are quite common, and because this syntax is a nuisance, a second structure access operator exists, tailored for use with structure pointers. It is the two-character -> operator:

pc->re = 3;

Formally, p->m is identical to (*p).m, for any structure pointer p and member m.

Another difference between structures and arrays is that unlike arrays, structures are “first class types” in C. It is possible to assign entire structures from one variable to another as a unit, and to pass and return them to and from functions. Comparison, however, is not supported; structures may not be compared for equality or inequality with the == and != operators. To compare structures, or to perform any other operations on them, typically requires writing functions. For example, here is a simple addition function, accepting two instances of struct complex and returning a third:

struct complex cpx_add(struct complex a, struct complex b)
{
       struct complex ret;
       ret.re = a.re + b.re;
       ret.im = a.im + b.im;
       return ret;
}

3.7.3. Unions

Closely related to structures are unions, which are somewhat analogous to Pascal’s variant records. Unlike most of C’s types, which (by the very definition of what a type is) can hold values of exactly one type, a union can hold values of several types, although only one value and one type at a time, and only those types that the programmer has declared as permissible. A union declaration is very similar to a structure declaration:

union anyval
       {
       char c;
       int i;
       long l;
       double d;
        };

Again, a definition of the form shown defines only a template, which in this case lists the permissible types that the union can hold and gives a name by which the value of each type can be accessed. Before the union can be used, a variable of the new type must typically be declared:

union anyval u;

Now, the member names access values within the union, using the same syntax as for structures: u.i accesses the integer stored in the union, u.d accesses the double, and so on.

Obviously, multiple values cannot be stored in a union at the same time; storing multiple values is what a struct is for. A union is just big enough to hold its largest member; the members all overlap. (In fact, an excellent way to think about a union is as a structure in which all members have offset 0, and therefore all overlap.) Also, there is no mechanism that automatically records which type of data has been most recently stored in a union; it is up to the programmer to remember somehow. One way is to wrap up a union and a code recording the current type, placing both elements in an enclosing structure:

struct taggedany
       {
       int which;
       union anyval value;
       };

The behavior is implementation defined if a value of one type is stored into a union and a different type is then extracted, but when it becomes necessary, for whatever reason, to inspect the bits that make up some value (such as a pointer or floating-point value) or otherwise “pun” unrelated types, the results are expected to be machine dependent, so a union is one way of achieving them.


Previous Table of Contents Next