Overview
Structs are used in C to define a new data type. That new data type is comprised of other primative types ( int, char, float, etc.) along with other structs. structs are used to group together different variables under a single name. This is particularly useful for organizing related data. Structs also make it easier to pass related data to functions. Instead of passing multiple parameters, you can pass a single struct.
Defining a struct
A struct is defined with the keyword struct
struct Node { uint16_t value; uint32_t *next; };
A struct is instantiated within a program like this:
void main (void) { struct Node myNode; }
Alternatively, I can use the keyword typedef to define a new data type and instantiate the structure using the name of the new data type.
#include <stdint.h> typedef struct{ uint16_t size; uint32_t *next; }Node; void main (void) { Node myNode; }
If your struct contains a pointer to the same type of struct that you are defining, you will need to declare the struct like this:
#include <stdint.h> typedef struct Node Node; struct Node{ uint16_t size; Node *next; }; void main (void) { Node myNode; }
Packed Vs. Unpacked Structs
When defining a structure, sometimes the fields in the structure are not word aligned. So what does it mean to be word aligned? Data is said to be word aligned if data’s address is a multiple of 4 bytes. Addresses 0x20000000, 0x20000004, 0x20000008, etc are all word aligned because the lowest nibble is a multiple of 4 (0x0, 0x4, 0x8, and 0xC). Addresses such as 0x20000001 are not aligned because the least significant nibble is not a multiple of 4.
So why does it matter if data is word aligned? When loading data from memory, it is more efficient in terms of clock cycles to load as words rather than as half words or bytes. In fact, if the data is word aligned, we can make use of the LDM/STM commands in some situations. Most compilers will automatically make your structures word aligned by inserting unused space for any struct that is not word aligned.
Looking at the following struct, you might be tempted to say that it requires 3 bytes of memory. In most situations, the compiler is going to want to word align this data so that it can more efficiently load/store the data from memory. In order to word align the struct, the compiler inserts an extra byte of data for each struct. This byte of data ensures that each struct of type Example_Struct will start on a word boundary. So in reality, the struct consumes 4 bytes of data.
typedef struct { uint8_t id; uint8_t type; uint8_t value; } Example_Struct;
In some situations though, we might not want to “throw away” an extra byte of data or there could be other requirements on the layout of the data where you do in fact want only 3 bytes of data per Example_Struct. In order for the compiler to create a non-word aligned struct, we need to use the compiler directive __packed.
typedef __packed struct { uint8_t id; uint8_t type; uint8_t value; } Example_Struct;
Initializing a struct
You can initialize the fields in a struct while instantiating it or as part of the program.
#include <stdint.h> #include <stdlib.h> typedef struct Node Node; struct Node{ uint16_t size; Node *next; }; // Initialize Array of Structs outside of a routine. // Note the additional set of "{" and "}" that // designate each element in the array Node MyNodeArray[] = { { 0, NULL }, { 0, NULL } }; void main (void) { //******************************** // Initialize during instantiation //******************************** // Single Struct Node MyNode = { 0, NULL, }; //******************************** // Initialize as assigment statements //******************************** MyNode.size=0; MyNode.next=NULL; MyNodeArray[0].size=0; MyNodeArray[0].next=NULL; MyNodeArray[1].size=0; MyNodeArray[1].next=NULL; }