Structs in C

Back in March 2014 I wrote an app in Python that would log in and check various OSPF properties.

When putting the data into a structure, I was limited both by knowledge and Python at the time. I ended up using a dictionary which worked rather well, but I was never 100% happy with it.

Recently I’ve stumbled across structs in C, in which I can make pretty much any data structure I would like.

The Python app above pulled some very specific results. Looks at each OSPF-enabled interface on a router and get it’s properties. Most of those properties are integers, but the interface name itself is a string. Each interface has n amount of properties, and each router itself has n amount of OSPF interfaces.

An interface therefore looks like so:

Interface name - String
IP address - String
Cost - Int
Adj - Int
Hello timer - Int
Dead timer - Int

And above that, a router looks like so:

Router name - String
Interface - Struct
Interface - Struct

A struct can itself contain structs. I’ll explain more on this later.

Struct

A struct is a data structure. Some amount of RAM is given back by the OS to contain the data you require. If we look back at the interface above, it has a certain amount of fields, and each of those fields contain different data types. Let’s create one in C:

struct ospfInt {
	char intName[15];
	char ipAddress[15];
	int cost;
	int adj;
	int hello;
	int dead;
}; 

Here we have created a data structure, that contains 2 strings with 16 characters, and 4 integers. Let’s use this in a program and manually insert properties into the struct for now. I’ll manually define two interfaces and then pass those structs to a function to print out the various properties:

#include <stdio.h>
#include <string.h>

struct ospfInt {
	char intName[15];
	char ipAddress[15];
	int cost;
	int adj;
	int hello;
	int dead;
};

void printInterface(struct ospfInt interface) {

	// Print out structs passed here
	printf("Interface name: %s\n", interface.intName);
	printf("IP Address: %s\n", interface.ipAddress);
	printf("Cost: %i\n", interface.cost);
	printf("Adj: %i\n", interface.adj);
	printf("Hello: %i\n", interface.hello);
	printf("Dead: %i\n\n", interface.dead);
}

int main(void) {

	// Create two interfaces with type struct ospfInt
	struct ospfInt interface1;
	struct ospfInt interface2;

	//Insert properties into those structs
	strcpy(interface1.intName, "ge-1/3/0.641");
	strcpy(interface1.ipAddress, "10.11.31.227");
	interface1.cost = 10;
	interface1.adj = 1;
	interface1.hello = 10;
	interface1.dead = 40;

	strcpy(interface2.intName, "lo0.0");
	strcpy(interface2.ipAddress, "10.11.225.224");
	interface2.cost = 0;
	interface2.adj = 0;
	interface2.hello = 3;
	interface2.dead = 12;

	// Print out properties via function
	printInterface(interface1);
	printInterface(interface2);
}

The end result of the above being like so:

$ ./structs
Interface name: ge-1/3/0.641
IP Address: 10.11.31.227
Cost: 10
Adj: 1
Hello: 10
Dead: 40

Interface name: lo0.0
IP Address: 10.11.225.224
Cost: 0
Adj: 0
Hello: 3
Dead: 12

typedef struct

Instead of initialising interface1 and interface2 above as type struct, I can just typedef the struct itself. So change this:

struct ospfInt {
	char intName[15];
	char ipAddress[15];
	int cost;
	int adj;
	int hello;
	int dead;
};

to this:

typedef struct {
	char intName[15];
	char ipAddress[15];
	int cost;
	int adj;
	int hello;
	int dead;
}ospfInt;

Now in order to create a structure of type ospfInt above, simply initialise it like so:

	// Create two interfaces with type struct ospfInt
	ospfInt interface1;
	ospfInt interface2;

Remembering to change all references to it to simply state ospfInt:

void printInterface(ospfInt interface) {

	// Print out structs passed here
}

attribute packed

C compilers will align data inside the structure to fit inside 4 byte structures. This has got to do with the way that data is read by CPUs these days. What this means though is that the amount of memory a structure of yours needs might not be the sum total of the data types inside. I’ll create a new structure called interface1 and check each of it’s components size. I’ll then print out the actual size on the system.

typedef struct {
	char intName[15];
	char ipAddress[15];
	int cost;
	int adj;
	int hello;
	int dead;
}ospfInt;

int main(void) {

	// Create an inteface with type struct ospfInt
	ospfInt interface1;

	// Print out size of each datatype inside struct
	printf("Bytes used in intName[15] = %lu\n", sizeof(interface1.intName));
	printf("Bytes used in ipAddress[15] = %lu\n", sizeof(interface1.ipAddress));
	printf("Bytes used in cost = %lu\n", sizeof(interface1.cost));
	printf("Bytes used in adj = %lu\n", sizeof(interface1.adj));
	printf("Bytes used in hello = %lu\n", sizeof(interface1.hello));
	printf("Bytes used in dead = %lu\n", sizeof(interface1.dead));
	printf("Bytes total = %lu", sizeof(interface1.intName) + sizeof(interface1.ipAddress)
									+ sizeof(interface1.cost) + sizeof(interface1.adj)
									+ sizeof(interface1.hello) + sizeof(interface1.dead));

	printf("\n\nActual byes used = %lu\n", sizeof(interface1));

This code gives this output on my Mac:

$ ./structs
Bytes used in intName[15] = 15
Bytes used in ipAddress[15] = 15
Bytes used in cost = 4
Bytes used in adj = 4
Bytes used in hello = 4
Bytes used in dead = 4
Bytes total = 46

Actual byes used = 48

The reason here is because the two strings are taking up 15 bytes each. As this is not on a four byte boundary, each one has been padded with an extra byte to take it to a multiple of four. If I were to change the struct to 16, these values should match up:

typedef struct {
	char intName[16];
	char ipAddress[16];
	int cost;
	int adj;
	int hello;
	int dead;
}ospfInt;
$ ./structs
Bytes used in intName[16] = 16
Bytes used in ipAddress[16] = 16
Bytes used in cost = 4
Bytes used in adj = 4
Bytes used in hello = 4
Bytes used in dead = 4
Bytes total = 48

Actual byes used = 48

You can inform the compiler not to align data this way. If you’re reading structures from somewhere else, it’s important that data values fall on boundaries that you would expect. To remove alignment you create the structure like so:

typedef struct {
	char intName[15];
	char ipAddress[15];
	int cost;
	int adj;
	int hello;
	int dead;
}__attribute__((__packed__))
ospfInt;

As alignment is removed, no padding will be done and the struct should be exactly 46 bytes:

$ ./structs
Bytes used in intName[15] = 15
Bytes used in ipAddress[15] = 15
Bytes used in cost = 4
Bytes used in adj = 4
Bytes used in hello = 4
Bytes used in dead = 4
Bytes total = 46

Actual byes used = 46

Struct of structs

The data types inside structs can be any data type, including other structs. I now want to create the router struct, which contains it’s name, it’s loopback IP address, and all the OSPF enabled interface structures. There are a couple of ways I could do this and I’ll go over both. Let’s first assume that a router will not have more than 500 OSPF enabled interfaces to begin with. I could create a struct of structs like so:

typedef struct {
	char routerName[20];
	char loopAddress[15];
	ospfInt interfaces[500];
}ospfRouter;

Create a function that will then print out the router details, plus its two interfaces like so:

void printRouter(ospfRouter router) {

	// Print out router information and pass
	// interfaces for printing
	printf("Router name: %s\n", router.routerName);
	printf("Loopback address: %s\n\n", router.loopAddress);

	for (int i = 0; i < 2; i++) {
		printInterface(router.interfaces[i]);
	}
}

int main(void) {
	printRouter(router1);
}

The result being:

$ ./structs
Router name: r1.com
Loopback address: 10.10.10.10

Interface name: ge-1/3/0.641
IP Address: 10.11.31.227
Cost: 10
Adj: 1
Hello: 10
Dead: 40

Interface name: lo0.0
IP Address: 10.11.225.224
Cost: 0
Adj: 0
Hello: 3
Dead: 12

Another way would be to define pointers in the router struct. This way you could create a load of interface structs, and have the router struct itself simply point to those variables. I can’t think of any benefit to this though. You would have to keep a load of interface variables and to me it seems it would be better to stick that inside the router struct itself.

Leave a Reply

Your email address will not be published. Required fields are marked *