The composite data type
Introduction
To start with, it is necessary to make definition of what is data type? As a usually, in complex words everything is described in Wikipedia:
Data type — is a classification identifying one of various types of data, such as real, integer or Boolean, that determines the possible values for that type; the operations that can be done on values of that type; the meaning of the data; and the way values of that type can be stored
In a nutshell, data type is a certain “instruction” for a compiler on how to represent data, how to store and handle it and what is possible to do with it.
The language data type can be divided into two categories: primitive and composite types.
Primitive data type — it is a data type, provided by the programming language as a basic built-in language unit. Also, this type is called as built-in or basic.
Composite data type –can be any type of data, which can be created in program as a combination of primitive and other composite data types.
If take a look get better with data types and read about it on Wikipedia it is possible to face with such expression:
Every programming language supports one or several built-in data types (basic types), besides, developed programming languages allows programmer to describe own data types, combining or extending already existed.
Let me remind you that some basic data types from C are already familiar to us:
- Symbolic — char;
- Integer — int;
- Real — float, double;
- Pointers — void *;
These ones seem to be quite clear. But what is with composite types in C?
The C language is related to the “high-developed programming languages”, so that it is possible to create users’ data types. Composite data type is a user’s type, but not every user’s data type is composite. How is it possible we will clarify a little bit later but now let us consider in practice what is composite data type.
Composite data type
In order to create composite data type in C, we need construction struct. Now, few seconds of Russian terminology: for definition struct in Russian language there is a special term “structure”. Cognitive dissonance occurs when it is necessary to formulate “structure of the structure may consist from nested structures”. I will try to avoid such expressions.
Structure is a construction with list of variables inside, which will be placed in the memory under one name and will be accessible through only one pointer. Somehow it is similar to array, but, in contrary, structure may include variables of different types.
The easier way to explain structure’s gist is by using this classic example:
int x;
int y;
};
Variables x and y are called fields of structures.
Now, alongside with built-in data types in C, we have new data type — struct Point. Yes, it has exactly such a name, long and a little bit strange, but we will fix it later.
Dimension of composite type
Let us conduct a little experiment and observe dimension of this new type:
#include <stdio.h>
int main () {
struct Point {
int x,y;
};
printf("%d\n",sizeof(struct Point));
return 0;
}
8
Dimension of struct Point is 8 bytes, but where this number came from? Inside of the structure there two variables of type int are declared, each of them is 4 bytes in size. i.e, the size of a structure depends on the number of declared variables in it.
Sometimes, compiler engages such sort of mechanism as adjusting. Its working principle is shown on the next example.
#include <stdio.h>
int main () {
struct Test {
int x
double y;
char z;
};
printf("%d\n",sizeof(struct Test));
return 0;
}Now, try to calculate size of struct Test before looking at the result in console.
Variables inside the structure are recommended to put in ascending order of its size, which can help save memory for no reason. Make this simple experiment with example above — change the order of variables.
Construction typedef
Once having declared structure user can utilize it as built-in data types, for example, declaring variables:
#include <stdio.h>
int main () {
struct Animal {
char name[10];
int age;
};
struct Animal hamster;
struct Animal snake;
return 0;
}
Such form of notation of variables declaration is quite bulky and may confuse user. It can be fixed.
For such purposes in C language there is special typedef construction. It enables to create user’s data type basing on already existing. In fact, this construction creates synonym of data type’s name. For instance, let us declare “new” int and call it ololo.
#include <stdio.h>
int main () {
typedef int ololo;// ololo is synonym for int
ololo x=45;
printf("%d\n",x);return 0;
}
This is why not every user’s type — composite. As it seen, “renamed” data types behaves exactly as its origins. This structure works properly with another structure:
#include <stdio.h>
int main () {
struct Animal {
char name[10];
int age;
};
typedef struct Animal Animal;
//but now we can write
Animal hamster;
Animal snake;
return 0;
}
Ninth line makes the brain to explode. It can be simplified by applying typedef directly to struct. In this case it is not necessary to specify structure’s name, such structure is called anonymous.
#include <stdio.h>
int main () {
typedef struct {
char name[10];
int age;
} Animal;
Animal hamster;
Animal snake;
return 0;
}
Captain obvious makes analogies:
typedef int ololo;
typedef struct {char name[10];int age;} Animal;
typedef
Well, now everything is clear and concisely. We have composite data type Animal, my congratulations. Let us proceed rankling struct.
Usage of composite data type
Composite data type is not much different in terms of its behaviour from basics.
The primary difference concludes in absence of built-in functionality at working with composite types. For instance, in order to compare two integer numbers we may use built-in language functionality, applying operator ==, but what about if we need to compare two structures of animal type? How to understand what exactly is necessary to compare? In such cases it is necessary to write required functional by yourself.
But let us not go ahead; let us start step by step.
Initialization at declaration
Let us consider how is it possible to declare and initialize variables of composite type:
#include <stdio.h>
int main () {
typedef struct {
char name[10];
int age;
} Animal;
Animal hamster = {"Bobby",3};
Animal snake = {"Alyona",12};
return 0;
}Talking the words I understand cannot!
Array’s initialization reminds it somehow, only the values of types different and in consequences in which they were established at structure’s declaration (Yoda(c)).
It is also possible to declare and initialize, for example, array of animals:
#include <stdio.h>
int main () {
typedef struct {
char name[10];
int age;
} Animal;
Animal hamster = {"Bobby",3};
Animal snake = {"Alyona",12};//array of structures
Animal animals[2]={hamster, snake};
return 0;
}
And save hamster pointer, for example:
#include <stdio.h>
int main () {
typedef struct {
char name[10];
int age;
} Animal;
Animal hamster = {"Bobby",3};
Animal snake = {"Alyona",12};
Animal animals[2]={hamster, snake};Animal *p=&hamster;
printf("%d\n",p); return 0;
}
It would be nice if we could somehow get sure that everything works fine and not only typing pointers. Actually, it is possible to output to the console hamster’s age. But how?
Access to the structure’s fields
Access to the structure’s fields occurs through access statement. Let us try to write the code that defines hamster’s age from hamster variable and from appropriate element of animal array.
#include <stdio.h>
int main () {
typedef struct {
char name[10];
int age;
} Animal;
Animal hamster = {"Bobby",3};
Animal snake = {"Alyona",12};
Animal animals[2]={hamster, snake}; printf("%d\n",hamster.age);
printf("%d\n",animals[0].age);return 0;
}
Thus, using the same approach it is achievable to change values of structure’s fields.
#include <stdio.h>
int main () {
typedef struct {
char name[10];
int age;
} Animal;
Animal hamster = {"Bobby",3};
Animal snake = {"Alyona",12};
Animal animals[2]={hamster, snake}; printf("%d\n",hamster.age); hamster.age=23;
printf("%d\n",hamster.age);
printf("%d\n",animals[0].age);return 0;
}
3
23
3
Amusing nuance occurred at outputting of 19 line. So, why array’s value did not change?
Structure’s transfer through pointers
During transmission of variable hamster in array, actually, it was copied. It means that now we have two instances of our “hamster” and we had cloned it successfully! One of them lives in main() under the name hamster and another one in array animals[], under the name animals [0]. Hence this is the reason of different hamster’s age in previous example. So what to do to prevent hamster from uncontrolled reproduction? Transfer through pointer.
Such approach is justified from saving memory prospective. Composite type can have quite a big size and constantly increasing number of its copies is right way to get stack overflow. So, at the initialization of animals array let us transfer there pointer for variables hamster and snake.
#include <stdio.h>
int main () {
typedef struct {
char name[10];
int age;
} Animal;
Animal hamster = {"Bobby",3};
Animal snake = {"Alyona",12};
Animal *animals[2]={&hamster, &snake}; printf("%d\n",hamster.age);hamster.age=23;
printf("%d\n",hamster.age);
printf("%d\n",animals[0]->age);return 0;
}
In case, when structure is being transferred though pointer — access to its fields is performed by another access statement: -> (arrow), as in 20th line. So now, everything is as it has to be: the same hamster in main() and animals[].
Let us take a look at address of hamster’s structure and its fields:
#include <stdio.h>
int main () {
typedef struct {
char name[10];
int age;
} Animal;
Animal hamster = {"Bobby",3};
Animal *p = &hamster; printf("%d\n",hamster.age);hamster.age=23;
printf("%p\n",p);
printf("%p\n",p->name);
printf("%p\n",&p->age);
printf("%d\n",sizeof(hamster));return 0;
}
0x7fff55a69c50
0x7fff55a69c50
0x7fff55a69c5c
16
As it seen, structure’s address matches with address of the first field — name, which is quite logical. And field’s address age comes with certain shift of 12 bits. In current example adjustment mechanism was engaged and instead of expected 10 bytes for field name the system allocated 12 bytes.
Since line in C, as an array, is an address itself, so that construction p->name ensures us with address at once. But the age field’s address it is required to receive by means of appropriate operator — &.
Structures and functions
It is worth noting, that no one restricts passing copy of the structure to the function. It has sense in that case if we only copy or if the structure is not big size. Let us consider situation with structure’s copy being passed to the function.
#include <stdio.h>
void printAnimal(Animal animal){
printf("%s is %d years old\n",animal.name,animal.age);
}int main () {
typedef struct {
char name[10];
int age;
} Animal; Animal hamster = {"Bobby",3};
Animal snake = {"Alyona",12};printAnimal(hamster);
printAnimal(snake);
return 0;
}
Now we have got compilation error:
error: unknown type name ‘Animal’
void printAnimal(Animal *animal) {
^
1 error generated.
It happens because function printAnimal() knows nothing about data type Animal, since it visibility scope is limited by main(). What to do next? The answer is to transfer declaration of user’s type upper all the functions, where it will be used.
#include <stdio.h>
typedef struct {
char name[10];
int age;
} Animal; void printAnimal(Animal animal){
printf("%s is %d years old\n",animal.name,animal.age);
}
int main () {
Animal hamster = {"Bobby",3};
Animal snake = {"Alyona",12};
printAnimal(hamster);
printAnimal(snake);
return 0;
}
Now another thing:
Bobby is 3 years old
Alyona is 12 years old
aving estimated situation, it is possible to understand that our data type Animal is not so small and to transfer each time a copy of variables of this type is quite costly.
So that it will be a better idea to do using pointer.
#include <stdio.h>
typedef struct {
char name[10];
int age;
} Animal; void printAnimal(Animal *animal){
printf("%s is %d years old\n",animal->name,animal->age);
} int main () {
Animal hamster = {"Bobby",3};
Animal snake = {"Alyona",12};printAnimal(&hamster);
printAnimal(&snake);
return 0;
}
Initialization after declaration
It is a common knowledge that C language developers do not provide with tools for working with users’ data types, which means that we should write everything by ourselves. Something we have already created — function printAnimal(), it would be nice to create another one function, which will initialize type Animal, because using “standard” approach it impossible.
#include <stdio.h>
typedef struct {
char name[10];
int age;
} Animal;void printAnimal(Animal *animal){
printf("%s is %d years old\n",animal->name,animal->age);
}int main () {
Animal hamster;
Animal snake; hamster = {"Bobby",3};
snake = {"Alyona",12};printAnimal(&hamster);
printAnimal(&snake);
return 0;
}
As the result we receive errors:
file.c:16:15: error: expected expression
hamster = {"Bobby", 3};
^
file.c:17:13: error: expected expression
snake = {"Alyona", 12}
^
2 errors generated.
So, let us write a function that will control hamster and snake from console. Take attention to the line number 9: if it is necessary to write a value at the certain address, then it requires taking address of the entire construction animal->age, not only animal, so that you may find brackets there.
#include <stdio.h>
typedef struct {
char name[10];
int age;
} Animal;void initAnimal(Animal *animal) {
scanf("%9s %d",animal->name,&(animal->age));
}void printAnimal(Animal *animal){
printf("%s is %d years old\n",animal->name,animal->age);
}int main () {
Animal hamster;
Animal snake;initAnimal(&hamster);
initAnimal(&snake);
printAnimal(&hamster);
printAnimal(&snake);
return 0;
}
Nested structures
In this article it has been mentioned that structure can contain in itself another structure in the form of field. Such field is called nested structure. The type of nested structure should be declared before. Besides, the structure cannot be nested into the structure of the same type, guess why?
Let us add to our animals field showing its position.
#include <stdio.h>
typedef struct {
int x,y;
} Point;typedef struct {
char name[10];
int age;
Point location;
} Animal;void initAnimal(Animal *animal) {
scanf("%9s %d",animal->name,&(animal->age));
scanf("%d %d",&(animal->location x),&(animal->location y));
}void printAnimal(Animal *animal){
printf("%s is %d years old",animal->name,animal->age);
printf("location at(%d,%d)\n",animal->location.x, animal->location.y);
}int main () {
Animal hamster;
Animal snake;initAnimal(&hamster);
initAnimal(&snake);
printAnimal(&hamster);
printAnimal(&snake);
return 0;
}
Bobby 41
1 2
Alyona 18
3 4
Bobby is 41 years old, located at (1, 2)
Alyona is 18 years old, located at (3, 4)
Finally, let us write a function that will change animal’s position.
#include <stdio.h>
typedef struct {
int x,y;
} Point;typedef struct {
char name[10];
int age;
Point location;
} Animal;void initAnimal(Animal *animal) {
scanf("%9s %d",animal->name,&(animal->age));
scanf("%d %d",&(animal->location x),&(animal->location y));
}void printAnimal(Animal *animal){
printf("%s is %d years old",animal->name,animal->age);
printf("location at(%d,%d)\n",animal->location.x, animal->location.y);
}void moveAnimal(Animal *animal, Point location) {
animal->location x=location.x;
animal->location y=location.y;
}int isEqualPoint(Point a, Point b) {
return a.x==b.x&&a.y==b.y;
}int main () {
Animal hamster;
Animal snake;initAnimal(&hamster);
initAnimal(&snake);
printAnimal(&hamster);
printAnimal(&snake);
moveAnimal(&snake,hamster.location);
printAnimal(&hamster);
printAnimal(&snake);
if (isEqualPoint(snake.location,hamster.location)) {
printf("Hamster %s is dead, ", hamster.name);
printf("snake %s is happy. \n", snake.name);
}return 0;
}
Bobby 12
1 2
Alyona 44
10 13
Bobby is 12 years old, located at (1, 2)
Alyona is 44 years old, located at (10, 13)
Bobby is 12 years old, located at (1, 2)
Alyona is 44 years old, located at (1, 2)
Hamster Bobby is dead, snake Alyona is happy.
Task
The task is to create structure car with fields: model, fuel and location. Since the car is delivered from the factory with empty tank it should be fueled (function refill). The car can drive (function move), if there is any gas — then drive and stop if the tank is empty. During the drive the gas consumption should be considerable.
The next part is to create several cars. Then assign to each of them certain model and initial coordinates. Next step is to fuel and set them to go. If the cars appear to be at the same time and in the same coordinates then a car accident has happened.
If output structure car to the console it will be shown: model, amount of gas and current coordinates.