These notes cover the use of statically allocated structures in C. If you are interested in learning how to use dynamically allocated structures, take a look at my Fall 2011 ECE 209 notes.
On the other hand, if you really want to use dynamically allocated structures, you might be better off using C++. You might lose a little on performance, but you’d gain a lot in reliability with the type-safe operators of C++. Finally, I will mention that dynamic allocation is generally avoided in embedded systems programming because of its unpredictable use of memory.
Heterogeneous data
char *Name[50] ; char Abbrev[50][2] ; int Population[50] ; Name[12] = "North Carolina" ; Abbrev[12][0] = 'N' ; Abbrev[12][1] = 'C' ; Population[12] = 9535483 ;
Is placing these values in three different arrays convenient, logical, or efficient?
Declaring and using structures
The C struct constructor allows the definition of a
heterogeneous data structures with fields or members
of different types.
The name of each field within a structure definition must be unique.
struct state {
char *Name ;
char Abbrev[2] ;
int Population ;
} ;
struct state NC ;
NC.Name = "North Carolina" ;
NC.Abbrev[0] = 'N' ;
NC.Abbrev[1] = 'C' ;
NC.Population = 9535483 ;
The dot operator
The dot operator joins a structure expression with a field or member name.
The . operator has the highest precedence level and has
left-to-right precedence. Other operators at this level are:
- Function call with
( ) - Array subscript with
[ ] - Field selection from object
. - Field selection from pointer
-> - Postfix
++ - Postfix
--
A horrendous example of this would be
f("United States")[30].Population++ used as a expression;
however, you do often find long sequences such as
USA[12].Name[0].
Structures as variables
In ANSI C, one structure can be assigned (with =) to another
and structures can be passed to and returned from functions.
This involves copying the entire structure and is reasonable only with
very small structures.
In C you can't return an array from a function, but you can return a structure containing an array. Now that is odd.
Initialization of structures
It is possible to initialize an entire structure with a single statement. Be careful to get all the fields in order.
struct state NC = { "North Carolina", {'N','C'}, 9535483 } ;
Types of structures
Effectively every struct definition
introduces a new programmer-defined type.
The typedef statement allows you to give a more
convenient name for the struct.
typedef struct state State ; State NC ;
The FILE “type” used for file I/O operations in C
is typically defined with a typedef of a C structure.
The quotes are used because
technically a typedef is not a new type, but rather
a “synonym” for an existing type.
By the way, the C structure is rather like a method-less class
in either C++ or Java. Also, in C++ you can avoid the typedef,
Arrays of structures
There is nothing unusual about arrays of structures.
struct state USA[50] ; USA[12].Name = "North Carolina" ; USA[12].Abbrev[0] = 'N' ; USA[12].Abbrev[1] = 'C' ; USA[12].Population = 9535483 ;
Nested structures
Structures nested a couple of levels can be useful and are quite common.
struct county {
char *Name ;
char Population ;
struct state State ;
} ;
struct county Haywood ;
Haywood.Name = "Haywood" ;
Haywood.State.Name = "North Carolina" ;
Structures, procedures, and pointers
Suppose you needed to write a function that increased the population of a state. Here is a first pass at that function.
void addPeople(State S, double growth) {
S.Population = (int)((1 + growth) * S.Population) ;
}
This won’t work because addPeople will be modifying
a copy of the state data structure and the modified copy
will be lost on the stack when addPeople returns.
There’s an additional problem: If the state data structure is
very large, there will be a non-trivial cost to copying the structure.
This action might even overflow the stack.
A better implementation would be to pass a pointer to the state structure with a call like the following:
addPeople(&NC, 0.134) ;
Because pointers to structures are frequency used in C, there is
a special operator -> (looks like a pointing sign)
in C. In C, X->f
is treated as syntactic sugar for
(*X).f .
With pointers and ->, the addPeople
function can be written as this:
void addPeople(State *S, double growth) {
S->Population = (int)((1 + growth) * S->Population) ;
}