|
|
|
|
|
|
|
|
|
|
|
Structures In C
|
|
|
A structure is a collection of one or more variables, possibly of different data types, grouped together under a single name for convenient handling. |
|
|
|
|
|
- Structure declaration
- Nested structures
- Arrays of structures
- Pointers to structures
- Structures and functions
- Unions
|
|
|
|
|
|
Structure declaration
|
|
|
A structure type is usually defined near to the start of a file using a typedef statement. typedef defines and names a new type, allowing its use throughout the program. typedefs usually occur just after the #define and #include statements in a file. |
|
|
|
|
|
Here is an example structure definition. |
|
|
|
|
|
1 typedef struct {
2 char name[64];
3 char course[128];
4 int age;
5 int year;
6 } student;
You could download file struct.c here
|
|
|
|
|
|
This defines a new type student variables of type student can be declared as follows. |
|
|
|
|
|
student st_rec; |
|
|
|
|
|
Notice how similar this is to declaring an int or float. The variable name is st_rec, it has members called name, course, age and year. |
|
|
|
|
|
Nested structures
|
|
|
Structures can contain other structures as members; in other words, structures can nest. Consider the following two structure types: |
|
|
|
|
|
1 struct first_structure_type {
2 int integer_member;
3 float float_member;
4 };
5
6 struct second_structure_type {
7 double double_member;
8 struct first_structure_type struct_member;
9 };
You could download file struct_nested.c here
|
|
|
|
|
|
The first structure type is incorporated as a member of the second structure type. You can initialize a variable of the second type as follows: |
|
|
|
|
|
1 struct second_structure_type demo;
2
3 demo.double_member = 12345.6789;
4 demo.struct_member.integer_member = 5;
5 demo.struct_member.float_member = 1023.17;
You could download file struct_nested1.c here
|
|
|
|
|
|
The member operator . is used to access members of structures that are themselves members of a larger structure. No parentheses are needed to force a special order of evaluation; a member operator expression is simply evaluated from left to right. |
|
|
|
|
|
In principle, structures can be nested indefinitely. Statements such as the following are syntactically acceptable, but bad style. (See Style.) |
|
|
|
|
|
1 my_structure.member1.member2.member3.member4 = 5;
You could download file struct_nested2.c here
|
|
|
|
|
|
What happens if a structure contains an instance of its own type, however? For example: |
|
|
|
|
|
1 struct regression {
2 int int_member;
3 struct regression self_member;
4 };
You could download file struct_nested3.c here
|
|
|
|
|
|
In order to compile a statement of this type, your computer would theoretically need an infinite amount of memory. In practice, however, you will simply receive an error message along the following lines: |
|
|
|
|
|
struct5.c: In function `main':
struct5.c:8: field `self_member' has incomplete type
|
|
|
|
|
|
The compiler is telling you that self_member has been declared before its data type, regression has been fully declared -- naturally, since you're declaring self_member in the middle of declaring its own data type! |
|
|
|
|
|
|
|
|
|
|
|
Arrays of structures
|
|
|
Just as arrays of basic types such as integers and floats are allowed in C, so are arrays of structures. An array of structures is declared in the usual way: |
|
|
|
|
|
1 struct personal_data my_struct_array[100];
You could download file struct_array.c here
|
|
|
|
|
|
The members of the structures in the array are then accessed by statements such as the following: |
|
|
|
|
|
The value of a member of a structure in an array can be assigned to another variable, or the value of a variable can be assigned to a member. For example, the following code assigns the number 1974 to the year_of_birth member of the fourth element of my_struct_array: |
|
|
|
|
|
1 my_struct_array[3].year_of_birth = 1974;
You could download file struct_array1.c here
|
|
|
|
|
|
(Like all other arrays in C, struct arrays start their numbering at zero.) |
|
|
|
|
|
The following code assigns the value of the year_of_birth member of the fourth element of my_struct_array to the variable yob: |
|
|
|
|
|
1 yob = my_struct_array[3].year_of_birth;
You could download file struct_array2.c here
|
|
|
|
|
|
Finally, the following example assigns the values of all the members of the second element of my_struct_array, namely my_struct_array[1], to the third element, so my_struct_array[2] takes the overall value of my_struct_array[1]. |
|
|
|
|
|
1 my_struct_array[2] = my_struct_array[1];
You could download file struct_array3.c here
|
|
|
|
|
|
Pointers to structures
|
|
|
Although a structure cannot contain an instance of its own type, it can can contain a pointer to another structure of its own type, or even to itself. This is because a pointer to a structure is not itself a structure, but merely a variable that holds the address of a structure. Pointers to structures are quite invaluable, in fact, for building data structures such as linked lists and trees. (See Complex data structures.) |
|
|
|
|
|
A pointer to a structure type variable is declared by a statement such as the following: |
|
|
|
|
|
1 struct personal_data *my_struct_ptr;
You could download file struct_ptr.c here
|
|
|
|
|
|
The variable my_struct_ptr is a pointer to a variable of type struct personal_data. This pointer can be assigned to any other pointer of the same type, and can be used to access the members of its structure. According to the rules we have outlined so far, this would have to be done like so: |
|
|
|
|
|
1 struct personal_data person1;
2
3 my_struct_ptr = &person1;
4 (*my_struct_ptr).day_of_birth = 23;
You could download file struct_ptr1.c here
|
|
|
|
|
|
This code example says, in effect, "Let the member day_of_birth of the structure pointed to by my_struct_ptr take the value 23." Notice the use of parentheses to avoid confusion about the precedence of the * and . operators. |
|
|
|
|
|
There is a better way to write the above code, however, using a new operator: ->. This is an arrow made out of a minus sign and a greater than symbol, and it is used as follows: |
|
|
|
|
|
1 my_struct_ptr->day_of_birth = 23;
You could download file struct_ptr2.c here
|
|
|
|
|
|
The -> enables you to access the members of a structure directly via its pointer. This statement means the same as the last line of the previous code example, but is considerably clearer. The -> operator will come in very handy when manipulating complex data structures. (See Complex data structures.) |
|
|
|
|
|
Structures :: typedef
|
|
|
There is an easier way to define structs or you could "alias" types you create. For example: |
|
|
|
|
|
1 typedef struct {
2 char *first;
3 char *last;
4 char SSN[9];
5 float gpa;
6 char **classes;
7 } student;
8
9 student student_a;
You could download file type_defs_c.c here
|
|
|
|
|
|
Now we get rid of those silly struct tags. You can use typedef for non-structs: |
|
|
|
|
|
1 typedef long int *pint32;
2
3 pint32 x, y, z;
You could download file type_defs1_c.c here
|
|
|
|
|
|
x, y and z are all pointers to long ints. typedef is your friend. Use it. |
|
|
|
|
|
Structures and functions
|
|
|
A structure can be passed as a function argument just like any other variable. This raises a few practical issues. |
|
|
|
|
|
Where we wish to modify the value of members of the structure, we must pass a pointer to that structure. This is just like passing a pointer to an int type argument whose value we wish to change. |
|
|
|
|
|
If we are only interested in one member of a structure, it is probably simpler to just pass that member. This will make for a simpler function, which is easier to re-use. Of course if we wish to change the value of that member, we should pass a pointer to it. |
|
|
|
|
|
When a structure is passed as an argument, each member of the structure is copied. This can prove expensive where structures are large or functions are called frequently. Passing and working with pointers to large structures may be more efficient in such cases. |
|
|
|
|
|
Structures and Unions
|
|
|
Unions are declared in the same fashion as structs, but have a fundamental difference. Only one item within the union can be used at any time, because the memory allocated for each item inside the union is in a shared memory location. Why you ask? An example first: |
|
|
|
|
|
1 struct conditions {
2 float temp;
3 union feels_like {
4 float wind_chill;
5 float heat_index;
6 }
7 } today;
You could download file struct_union_c.c here
|
|
|
|
|
|
As you know, wind_chill is only calculated when it is "cold" and heat_index when it is "hot". There is no need for both. So when you specify the temp in today, feels_like only has one value, either a float for wind_chill or a float for heat_index. Types inside of unions are unrestricted, you can even use structs within unions. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|