Embedded C: Struct and Union
In this article we are going to discuss a couple of complex data structures in C language. Understanding struct and union is not only essential in learning C language in general, but also a vital need in embedded programming because they can make the life of an embedded developer much easier with abilities uniquely tuned up for this purpose. In the next part we will see some usage cautions and some real-life applications.
Structure (Struct)
You can think of a struct as a way to aggregate a number of different variables of different data types in one entity that starts in a specific address in memory. Structures in C language are declared using the keyword struct :struct example_struct { unsigned int member1; float member2; char* member3; };
Note : pay attention to the semicolon after the closing bracket.
A struct is a custom data type (user-defined) and the data fields in a struct are called members. You can declare and define structure variables after you declare the structure itself:
struct example_struct foo; struct example_struct bar = {4,1.4,”hello world”};
To declare and define at the same time, use:
struct example_struct { unsigned int member1; float member2; char* member3; } foo, bar;
Structure’s data members are accessible using the “.” operator:
struct example_struct foo; foo.member1=2
Of course, pointers are widely used and to access data members in pointers, the “->” operator is used:
struct example_struct *foo; foo->member1=2;
Note: a struct only resembles a template and the compiler won’t allocate any memory when you declare one. It only does that when you define a struct variable:
struct example_struct foo;
(It is required to use the struct keyword in this case, but we are going to show you how to define a new struct variable without it later in the post)
In some cases, only a number of bits is needed for a struct data member. For this purpose, you can use bit fields, which are designed specifically to reduce the needed memory amount to a minimum. To declare bit fields, use the “:” operator followed by the number of bits as an integer value:
struct example_struct { int element_a : 1; int element_b : 3; int element_c : 2=0; };
In this example, element_a is one bit long, element_b is three bits and element_c is two bits with a default value set to 0.
It is not advisable to use bit fields because it is not a standard and may not be supported by all compilers in an identical way. However, this method can be used if you mind this particular point.
Unions
In some cases, you may need a type of variables that can be accessed in several ways (as several data types) and in the same memory location at the same time. Unions are used to provide this type of variables.Declaring unions is similar to declaring structs using the union keyword:
Union union_tag { unsigned int member1; Float member2; char* member3; }union_name;
Also, accessing data members is similar to structs:
union union_name foo; foo.member1=2;
In case of pointers:
struct union_name *foo; foo->member1=2;
The fundamental difference between the two is how data is allocated in memory. In structures, the compiler would allocate memory for each data member within the struct. While in unions, the compiler would allocate a block of memory equal to the largest data member in the union.
Using unions in software programming may not be very popular, but it is an important aspect in embedded programming. Using it as a type of a temporary memory or “scratch pad” is one of the common uses in embedded development. This can be done when it is needed to have a space in the memory to access and store different variable types (while no two of them are used at the same time). This is useful in embedded systems to reduce memory consumption.
union temp{ int as_int; long as_long; char as_char; short as_short; } scratch_pad;
Unions are also used to extract small blocks of data from a larger one:
union timer { uint32 val; // Timer Register 32-bit-long struct { uint8 Three; uint8 Two; uint8 One; uint8 Zero; } Bytes; } TMR;
In this example, you can access the timer’s 32-bit as one piece (TMR.val). Alternatively, you can access each byte individually (TMR.Bytes.Zero).
Using Struct and Union With Typedef Command
Using typedef command, you can define your own variable type. For example:typedef unsigned long ulong;
We used ulong name as an alias for unsigned long. Now we can use ulong to declare variables with unsigned long type.
The same thing applies with struct and union:
typedef struct { type member_a; type member_b; type member_c; } sname ;
Now we can use sname to declare variables s1 and s2 with the defined struct type:
sname s1,s2;
References