Tutorial five – structs

This is the fifth tutorial in the series Online C Tutorial for beginners and is about struct variables. These are single page bite size tutorials which can usually be run on either codepad.org or ideone.com.

Structs in C

Previously we’ve used variables either individually or in arrays as in

int numships = 10;

float accounts[20];

But what if we wish to group a collection of diverse variables into one? Examples where this would prove useful include:

  • Payroll Record- name, department, salary etc..
  • A spaceship in a game. Mass, crew, acceleration, capacity…
  • A room in a dungeon game. It’s got doors, description, treasures, monsters…

There are many more. Almost all programming languages have something similar (except Basic!). So how do we define and use structs?

#include <stdio.h>
struct starship {
  char * name;
  char * type;
  int numphasers;
  int numphotontorps;
  int crewsize;
};

int main (int argc, char * args[]) {
  struct starship enterprise = {"Enterprise D", "NCC 1701", 5, 25, 428 };
  printf("Ship's name is %s",enterprise.name) ;
return 0;
}

When run, the output is:

Ship's name is Enterprise D

This example lumps together two C strings and three ints into a type called starship. When you declare an instance of this struct type, the C compiler expects the word struct in front of the declaration. You can add the word const in front to make it immutable. Any attempt to change the fields of a const starship field will fail.

Arrays and Structs

To remind you; the previous tutorial was all about arrays.

C expressions and statements are compiled through a method called recursive descent. In a nutshell, this means that variable declarations can use different data types, so it’s quite possible to have a type that has a struct containing an array of something. You could then declare an array of these structs that contain arrays etc.

If we create a fleet struct that has a name and up to 100 starships then declare an array of ten fleets, it will look something like this:


struct fleet {
char * name;
struct starship ships[100];
};< struct fleet fleets[10];/code>

The full code for this, and copying the enterprise to ship[0] in fleet[0] is this:


#include <stdio.h>

struct starship {
char * name;
char * type;
int numphasers;
int numphotontorps;
int crewsize;
};

struct fleet {
char * name;
struct starship ships[100];
};

struct fleet fleets[10];

int main (int argc, char * args[]) {
struct starship enterprise = {“Enterprise D”, “NCC 1701″, 5, 25, 428 };
fleets[0].name=”First fleet”;
fleets[0].ships[0]= enterprise;

printf(“Ship’s type is %s”,fleets[0].ships[0].type) ;
return 0;
}

And running this outputs:

Ship's type is NCC 1701

In that example, none of the other ships in fleet[0] or any in any other fleet has any valid data. It’s your responsibility to correctly initialise data in structs and in any variable.

Try to think of the struct as a variable in itself. So we can have an instance of a starship or of a fleet. Getting the right data structure is a key point in developing software.

Accessing Fields in Structs

I prefer to call the individual variables that make up a struct as fields of that struct. If there is a name for the struct variable then you can access the fields using the dot notation.

For instance in the first example, the numphasers field in the starship struct is set to 5 in Enterprise.

You can change it with code like this:

enterprise.numphasers = 10;

A handy feature of C is the ability to copy structs through simple assignment as the line in the second example showed:


fleets[0].ships[0]= enterprise;

However this contains a potentially nasty bug as assignment like this just copies the memory. Let’s take a look under the hood first.

Add this line to the second example, just after the first printf.

printf("\n\rSize of Enterprise = %d Sizeof Fleets = %d",sizeof(enterprise),sizeof(fleets));

Compiling and running as 32 bit, this line outputs:


Size of Enterprise = 20 Sizeof Fleets = 20040

The starship struct contains five fields, each 4 bytes in size and two of these are C-Strings which are each a pointer to an array of text followed by a terminating 0 value.

By doing a simple assignment, we made a shallow copy of the two strings. So now both the enterprise variable and fleets[0].ships[0] contain pointers to the same strings. A shallow copy is essentially a bit-by-bit copy of the struct. It doesn’t copy things like strings (technically in C these are char * pointers).

In the Visual Studio debugger I examined both enterprise and fleets[0].ships[0] and the values (of the pointer) for name and type in both structs are shown to be identical.

enterprise {name=0x00f85750 "Enterprise D" type=0x00f857d0 "NCC 1701" numphasers=5 ...}
fleets[0].ships[0] 0x00f874c4 {name=0x00f85750 "Enterprise D" type=0x00f857d0 "NCC 1701" numphasers=5 ...}

If any string operations were done on these fields in fleets[0].starships[0] then it would also affect the strings in enterprise as well.

That’s it for structs though we’ll revisit them in the next few tutorials when I start looking at pointers.

Link to previous tutorial.

Link to next tutorial.

(Visited 7,821 times, 8 visits today)