So far, we have been concentrating on primitive types and arrays
But there is a need to group data values together into 'meaningful' structures
For example, a bird in an angry birds game might have variables for its position, its velocity, and maybe other values such as a size or a colour, yet you want to treat it in a program as a single 'object'
The mechanism in C to group several values together into a single entity is
the struct
This represents the first step in the process of developing 'object oriented programming', which will be explored in the Java unit
Suppose you have a 2-D graphics program, and in it a bird has a pair of x-y coordinates giving its position
Then you can declare a bird structure like this:
struct bird { int x, y; };
Warning: don't forget the semicolon after the close curly bracket (it is like an initializer, not like an if
or while
block)
/* Struct demo */ #include <stdio.h> // A bird 'object' looks like this struct bird { int x, y; }; // Move a bird a bit then print int main() { struct bird b = {41, 37}; b.x++; b.y = b.y + 5; printf("%d %d\n", b.x, b.y); }
The struct
declaration creates a new type
struct bird { int x, y; };
This is the type struct bird
of bird variables
Each bird variable has two int
fields (sub-variables) called x
and y
New variables can then be created
struct bird b = {41, 37};
In this case, the variable b
is initialized by specifying its fields in an initializer (don't forget the semicolon)
Fields are accessed using the dot (.
) notation
b.x++; b.y = b.y + 5;
The field b.x
is incremented by one, and b.y
has 5
added to it.
/* Struct demo: doesn't work */ #include <stdio.h> struct bird { int x, y; }; // Move a bird by a given amount void move(struct bird b, int dx, int dy) { b.x = b.x + dx; b.y = b.y + dy; } // Move and print int main() { struct bird jay = {41, 37}; move(jay, 1, 5); printf("%d %d\n", jay.x, jay.y); }
The failed experiment shows that structs are passed by value
In the example, the variable jay
is copied into the argument
variable b
, so changes to b
do not affect
jay
This is different from arrays, presumably because structs are typically small
The struct declaration has been abbreviated (to fit the example onto one slide)
struct bird { int x, y; };
Both semicolons are still needed
/* Struct demo */ #include <stdio.h> struct bird { int x, y; }; // Move a bird by a given amount struct bird move(struct bird b, int dx, int dy) { b.x = b.x + dx; b.y = b.y + dy; return b; } int main() { struct bird jay = {41, 37}; jay = move(jay, 1, 5); printf("%d %d\n", jay.x, jay.y); }
In the example we looked at, returning a structure was an annoyance - it was just to allow us to write functions which update structures temporarily, before we get to pointers
But it is a good solution to a common problem: writing a function which finds two things
So if position(...)
returns the position of something, a
structure with x
, y
fields can be returned
In practice, most programmers want to pass structs by reference, not by value, to avoid the cost of repeatedly copying the fields to and fro, and to allow functions to update the fields directly
That's done by passing pointers to structs instead of the structs themselves (see pointer chapter)
That will be a second step towards object oriented programming
Typedefs let you avoid using the struct
keyword
so often:
struct bird { int x, y; }; typedef struct bird Bird; ... Bird move(Bird b) { ... } ... int main() { Bird jay = {41, 37}; ... }
A typedef
doesn't define a type:
typedef struct bird Bird;
It defines a type synonym - another name for an existing type
The new name Bird
comes at the end (as if you were declaring a
variable), defined as a synonym for struct bird
Don't leave out the final semicolon
Often the same name is used:
typedef struct bird bird;
Technically, there are now two identical names bird
But one only ever comes straight after struct
, and the other
never does, so the compiler can tell them apart
It is possible to have a variable which has the same name as a typedef:
bird bird; bird.x = ...;
The compiler can tell the two names apart because one only ever appears in 'type' positions, and the other only appears in 'variable' positions
But you can't do that for built-in type names:
int int = 42;
To analyse a document, counting the number of times each word appears, you could use a structure like this:
struct word { char s[10]; int count; };
The array field s
holds the word as a string
It has to be fixed length (a compile-time constant) so that the C compiler can do type and size analysis
We will see how to make it variable length in the memory chapter
Sometimes, you want to provide mostly readonly-access to a structure
A neat way to do it is this:
struct word { char s[10]; int count; }; typedef struct word const word;
From now on, the type word
provides readonly access, whereas
the type struct word
provides full access