210 likes | 387 Views
Structures & Unions. User-Defined Types. C provides facilities to define one’s own types. These may be a composite of basic types ( int , double , etc) and other user-defined types. The most common user-defined type is a structure, defined by the keyword struct .
E N D
User-Defined Types • C provides facilities to define one’s own types. • These may be a composite of basic types (int, double, etc) and other user-defined types. • The most common user-defined type is a structure, defined by the keyword struct. • Another, less common, form of user-defined type is a union, which will be discussed in the next lecture.
Structures • A structure is a collection of one or more variables, possibly of different types, grouped together under a single name for convenient handling (K&R page 127). • Structures are user-defined aggregate types. • They assist program organisation by • Grouping logically related data, and giving this set of variables a higher-level name and more abstract representation. • Enabling related variables to be manipulated as a single unit rather than as separate entities. • Reducing the number of parameters that need to be passed between functions. • Providing another means to return multiple values from a function.
Structure Syntax • A structure is defined by the keyword struct followed by a set of variables enclosed in braces. • Consider the following structure to represent 2-D points. struct Point { int x; int y; }; • The variables x and y are called members of the structure type Point. • Style note: Structures are given names with Capital first letters, by convention. Distinguish them from variables and functions (lowercase first letter), and symbolic constants (all uppercase).
Declaring Structure Variables • There are several equivalent ways to define variables of a particular structure type. • Declare them at the structure definition. struct Point { int x; int y; } p1, p2, p3; /* Define 3 variables */ If these 3 variables were the only definitions of type Point, and the type is not used again, we may well have written struct { int x; int y; } p1, p2, p3; /* Define 3 variables */ • Define the variables at some point after the structure definition. struct Point p1, p2, p3; /* Define 3 variables */
Initialising Structure Variables • A structure may be initialised when it is defined using brace notation. struct Point topleft = {320, 0}; • The order of values in the initialiser list matches the order of declarations in the structure.
Accessing Members • Members of a structure type may be accessed via the “.” member operator. struct Point topleft; topleft.x = 320; topleft.y = 0; struct Point delta = { pt1.x - pt2.x, pt1.y - pt2.y }; double distance = sqrt((double)delta.x * delta.x + (double)delta.y * delta.y);
Nested Structures • Structures may be defined inside other structures. struct Rectangle { struct Point topleft; struct Point bottomright; }; • To access lower-level members, need to use member operator multiple times. struct Rectangle rect; rect.topleft.x = 50;
Operations on Structures • Operations permitted on structure types is a subset of those permitted on basic types. • Structures may be copied and assigned, but not compared. struct Point p1 = { 0, 0 }; struct Point p2; p2 = p1; /* Valid. */ if (p1 == p2) /* Invalid.*/ printf("Points are equal\n"); if (p1.x == p2.x && p1.y == p2.y) /* Valid. */ printf("Points are equal\n");
Operations on Structures • Structures may be passed to functions and returned from functions. (This is a consequence of being copyable.) struct Point point_difference(struct Point p1, struct Point p2) /* Return the delta (dy, dy) of p2 with respect to p1. */ { p2.x -= p1.x; p2.y -= p1y; return p2; } • As for other variables, structures are passed-by-value. So, the second argument in the calling function will not be affected by the changes to p2.
Pass by Reference • Pass by value may be very inefficient if the structure is large (ie, has many members). • Passing a pointer to a structure is generally much more efficient than making a copy of the structure itself. • Defining pointer types is the same as for variables of primitive types. struct Point pt = { 50, 50 }; struct Point *pp; pp = &pt; (*pp).x = 100; /* pt.x is now 100. */
Pointers to Structures • Notice the parentheses around the dereferenced pointer. (*pp).x = 100; • This is necessary to enforce correct precedence. • An alternative notation permits simpler pointer access to structure members. (*pp).x = 100; pp->x = 100; /* equivalent */ • Another example, struct Rectangle rect, *pr = ▭ rect.topleft.x = 50; /* equivalent operations */ (*pr).topleft.x = 50; pr->topleft.x = 50;
Arrays of Structures • The definition of arrays of structure types is virtually identical to arrays of primitive types. struct Point pa[10]; • Structure arrays can be initialised with an initialiser list enclosed in braces (and the size is determined by the compiler if unspecified). struct Point pa[] = { {0, 0}, {0, 240}, {320, 240}, {320, 0} };
Structure Arrays • The size of a structure in bytes may be found via the sizeof operator. • The usual C idiom may be used to compute the number of elements in an array of structures. struct Point pa[] = { {0, 0}, {0, 240}, {320, 240}, {320, 0} }; struct Point *pp = pa; for (; pp != pa + sizeof(pa) / sizeof(pa[0]); ++pp) printf("%d %d\n", pp->x, pp->y);
Structures and Pointer Arithmetic • As with primitive types, the compiler knows the size of a structure and, given a pointer of a structure type will compute the appropriate address offsets automatically. • Important. The size of a structure need not equal the sum of its constituent parts. • Most real machines have alignment restrictions for certain types (e.g., integers must be located at even addresses). • The compiler will pad a structure with unnamed “holes” to ensure each member is properly aligned. Eg, the following structure might not have size 5-bytes, but 6 or 8-bytes. struct MyStruct { char c; int i; }; • The sizeof operator returns the correct size.
Self-Referential Structures • A structure may not contain an object of its own type. struct Node { int item; struct Node n; /* Invalid */ }; • In general, a structure may not contain an object of an incomplete type. • However, a structure may contain a pointer to an incomplete type. struct Node { int item; struct Node *pn; /* Valid */ };
Self-Referential Structures • The ability to refer to (ie, point to) an incomplete type, including itself, is an important property for constructing a variety of data-structures. • For example: linked-lists, binary trees, graphs, hash tables, and more.
Example: A Linked List • Linked lists come in two basic varieties: singly linked and doubly linked. • We describe here a simple version of a singly linked list. • List consists of a set of nodes, where each node contains an item and a pointer to another list node. struct List { int item; struct List *next; }; • (Here we have chosen an int as the contained item. Any other type(s) may be used.)
Unions • Unions look similar to structures. They have identical declaration syntax and member access, but they serve a very different purpose. union Utype { int ival; float fval; char *sval; }; union Utype x, y, z; • Accessing members of a union is via “.” member operator or, for pointers to unions, the -> operator.
Unions • A union holds the value of one-variable at a time. • The compiler allocates storage for the biggest member of the union. • The type retrieved from the union must be the type most recently stored. Otherwise, the result is implementation dependent. union Utype x; x.fval = 56.4; /* x holds type float. */ printf("%f\n", x.fval); /* OK. */ printf("%d\n", x.ival); /* Implementation dependent. */
Unions • Unions are used to store one of a set of different types. • Commonly used to implement a “variant” array. (This is a form of generic programming.) • There are other uses also, but they are quite advanced (e.g., concern the alignment properties of unions).