16. STRUCTURES, UNIONS, AND ENUMERATIONS
Declaring Structures A structure is a collection of one or more components (members), which may be of different types. (Structures are called records in many other programming languages; members are known as fields.) Declaring structure variables can be done as follows: struct { char name[25]; int id, age; char sex; } s1, s2;
Declaring Structures Member names don’t conflict with any other names in a program. Structure variables may be initialized: struct { char name[25]; int id, age; char sex; } s1 = { "Smith, John", 2813, 25, 'M'}, s2 = { "Smith, Mary", 4692, 23, 'F'};
Structure Tags A structure can be given a name (a structure tag) for later use: struct student { char name[25]; int id, age; char sex; }; struct student s1, s2; The two declarations can be combined if desired, with the same effect: struct student { char name[25]; int id, age; char sex; } s1, s2;
Structure Tags As an alternative, we can use typedef to name a structure: typedef struct { char name[25]; int id, age; char sex; } Student; Student s1, s2;
Accessing the Members of a Structure The members of a structure are accessed by writing first the name of the structure, then a period, then the name of the member: struct student { char name[25]; int id, age; char sex; } s; strcpy(s.name, "Doe, John"); s.id = 18193; s.age = 18; s.sex = 'M';
Accessing the Members of a Structure Arrays and structures can be nested within one another: struct student class[30]; strcpy(class[12].name, "Doe, John"); class[12].id = 18193; class[12].age = 18; class[12].sex = 'M';
Operations on Whole Structures C allows three operations on whole structures. Assignment of whole structures: s1 = s2; /* s1 and s2 are structures of the same type */ Passing a whole structure to a function: f(s1); Returning a whole structure from a function: s1 = f(s2); No other operations are allowed, including testing whole structures for equality.
Operations on Whole Structures All three operations involve copying an entire structure, which can be time consuming if the structure is large.
Unions A union is similar to a structure, except that its members are overlaid (located at the same memory address). Example: union { int i; double d; } u; The members of a union are accessed in the same way as members of a structure: u.i = 15; or u.d = 8.89; Since u.i and u.d have the same memory address, changing the value of one alters the value of the other.
Unions Unions have the same properties as structures: Unions can be initialized (the initializer specifies the value of the first member). Unions can be identified by tags or type names. Unions can be assigned, passed to functions, and returned by functions. Uses of unions: To save space. To view a data item in two or more different ways. To build heterogeneous data structures.
Enumerations An enumeration is a collection of named integer constants. Enumerations are defined in a manner similar to structures and unions: enum bool {FALSE, TRUE}; enum rank {TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE}; enum suit {CLUBS, DIAMONDS, HEARTS, SPADES}; enum EGA_colors {BLACK, BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, LT_GRAY, DK_GRAY, LT_BLUE, LT_GREEN, LT_CYAN, LT_RED, LT_MAGENTA, YELLOW, WHITE};
Adding a “Tag Field” to a Union There is no way to tell which member of a union was last assigned. To retain this information, we can create a structure with two members: (1) the union itself, and (2) a “tag field” whose value indicates which member of the union was last assigned. struct { enum {INT_KIND, DOUBLE_KIND} kind; /* tag field */ union { int i; double d; } u; } s; The tag field is often an enumeration.
Adding a “Tag Field” to a Union When we assign to a member of the union, we also set the kind member: s.kind = INT_KIND; s.u.i = 15; We can test the kind member before accessing the union: if (s.kind == INT_KIND) printf("%d", s.u.i); else if (s.kind == DOUBLE_KIND) printf("%g", s.u.d);
New Features in C99 C99 has two new features that are related to structures and unions: Designated initializers Compound literals These features can also be used with arrays.
New Features in C99 Here is the usual way to initialize a structure: struct { int i; double d; } s = {1, 2.5}; Within a designated initializer, the values of the members can be listed in any order, provided that each value is accompanied by a member name: struct { int i; double d; } s = {.d=2.5,.i=1}; Any members that are not listed in the initializer will be given a default value.
New Features in C99 In the case of a union, only one member can be initialized: union { int i; double d; } u = {.d=2.5}; A designated initializer for an array looks like this: int a[10] = {[5]=6, [8]=2, [1]=3}; which is equivalent to writing int a[10] = {0, 3, 0, 0, 0, 6, 0, 0, 2, 0};
New Features in C99 A designated initializer may also contain values in ordinary positional notation. Using compound literals, it becomes possible to create and initialize structures, unions, and arrays “on the fly,” without storing them in variables. A compound literal consists of a type name in parentheses (similar to a cast expression), followed by a set of values in braces (similar to an initializer).
New Features in C99 In the following example, a point structure is being created and passed to the move_to function: move_to((struct point){5, 10}); In older versions of C, it would be necessary to create a name for the structure before passing it to the function: struct point pos = {5, 10}; move_to(pos); The following example creates a three-element array and sets p to point to the first element in the array: int *p = (int []){1, 2, 3}; By default, a compound literal is an lvalue, so its values can be changed.