Definition
Aggregate data types can store more than one individual data type simultaneously. C provides two types of aggregate data types: arrays and structures. Arrays store collections of elements of the same type, while structures can hold collections of different types. However, unlike arrays, structure members cannot be accessed via subscript because each element of an array has the same length, whereas structure elements can have varying lengths. Structure types are scalar types.
Tag declarations and typedef are used. Member names in one structure can be the same as member names in another structure.
Direct access to structure members is done using the dot operator (.). The dot operator has left-to-right associativity. The subscript operator and dot operator have the same precedence and both associate left-to-right.
For indirect access to structure members, since the dot operator has higher precedence than the indirection operator, parentheses must be used in expressions. Alternatively, the -> operator can be used for indirect access.
Self-referential Structures
A structure cannot contain a variable of its own type because that would lead to infinite recursion. However, the program allows a pointer to the structure type, because when the structure size is determined, the pointer size is already fixed.
typedef struct {
int a;
short b[2];
} ex2;
typedef struct ex {
int a;
char b[3];
ex2 c;
struct ex *d;
} ex;
Structures, Pointers, and Members
Consider the following example:
ex x = {10, "Hi", {5, {-1, 25}}, 0};
ex *px = &x;
px; // pointer variable
// The rvalue of px is the value of px, i.e., the address of structure variable x
// The lvalue of px is the memory location of px; since px is not an address constant, it can be assigned
*px; // indirect access to the memory pointed by px
// The lvalue of *px is the value of the structure pointed to by px, which can be modified
// The rvalue of *px is the value of the structure, which can be assigned or accessed
// *px + 1 is illegal because there is no operation defined for adding an integer to a structure
// *(px + 1) is also illegal; x is a scalar
px->a; // arrow operator performs indirect access
// a is the first member of the structure; its address is the same as the address of px
// But their types are different: the former is an integer, the latter is a pointer to a structure
// The -> operator has higher precedence than the & operator
// int *pi = &px->a;
px->b; // pointer constant
px->c; // access nested structure
*px->c.b; // access the first element of array b inside structure c
Memory Allocation for Structures
The size of a structure is determinedd by the member with the largest memory requirement, because it imposes the strictest boundary alignment. Consider the following three cases:
typedef struct {
char a;
int b;
char f;
} c; // sizeof(c) is 12 bytes, because a and f each occupy 4 bytes to ensure b's address is divisible by 4
typedef struct {
int b;
char a;
char f;
} c; // sizeof(c) is 8 bytes, because a and f share 4 bytes, and b occupies 4 bytes
typedef struct {
int b;
char a;
char f;
} c; // same reason as above
Structures as Function Parameters
Due to memory allocation, when a structure is passed as a function parameter, the byte count can be relatively large. Therefore, a pointer can be used to parameter passing, and indirect access operations can be performed inside the function. However, the downside is that it can affect the original structure in the calling program, but this can be mitigated by adding const.
Bit-fields
Another application of structures is bit-fields. Bit-field declarations are similar to structure declarations, but their members are fields of one or more bits.
Bit-field declarations are similar to normal structure declarations, but there are two differences: first, the types of bit-field members must be one of int, signed int, or unsigned int; second, a colon and an integer value are placed after the member name, where the integer value indicates the number of bits occupied by the bit-field.
Bit-fields can pack data of odd lengths together, saving storage space.
Using bit-fields is for convenience; any task that can be accomplished with bit-feilds can also be done using shift and mask operations.
Unions
Union declarations are similar to structure declarations, but all members of a union refer to the same memory location. The memory size of a union is determined by the largest member in the union.
Variant records: A specific area in memory will store values of different types at different times.