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) ; }