Structures
In this lesson, you learn how to create data types made up of many smaller elements - what in C++ we call structures.
Motivation

If, for example, when creating a game 🎮, we want to include opponents in our program, we will usually have to write down some information about each of them.
Consider what data about the enemies in the game can be useful? It can be, for example:
- name 👾
- health 💚
- strength 💪
etc.
Using the knowledge we have acquired so far, if we wanted to write a program that stores this information, we could do it like this:
#include <string>
int main() {
std::string enemyName = "Goblin";
float enemyHealth = 50;
float enemyStrength = 12;
// ...
}
When we want to have more opponents in the game, we will encounter a problem, or rather inconvenience:
If we use multiple arrays for this purpose:
std::vector< std::string > enemyNames;
std::vector< float > enemyHealth;
std::vector< float > enemyStrength;
then each opponent will be described under the same index in these tables:
enemyNames[ index ]
describes the nameenemyHealth[ index ]
describes life pointsenemyStrength[ index ]
describes the points of strength
This method involves "spreading" information about a single opponent across multiple tables.
Adding one enemy to a set in such a program would look like this:
enemyNames.push_back("Goblin");
enemyHealth.push_back(50);
enemyStrength.push_back(10);
The more information we want to store about our opponents, the more bothersome it will be. Fortunately, structures come to our aid here.
Creating structures
Let's recall what data we need to store:
- name 👾
- life 💚
- strength 💪
We are about to add a structure that allows us to create an object that contains these 3 things.
#include <string>
struct Enemy
{
std::string name;
float health;
float strength;
};
int main()
{
// We leave it empty for now
}
The above code introduces a new structure - Enemy
.
A structure is a description, a pattern, a recipe for how to create an object (in this case, an enemy).
To create a structure, we write its name after the struct
keyword, then we put its contents
between the curly braces {
and }
.
The contents can be, for example, variables.
Note the obligatory semicolon after the curly braces to close the structure definition:
struct Enemy
{
std::string name;
float health;
float strength;
};
Objects
Here's how to create an object that uses the Enemy
structure
(which is treated as a formula for creating an object):
// prism-push-types:Enemy
int main()
{
Enemy boss;
}
Thus, we contained all these 3 fields (name
, health
and strength
) inside one variable boss
.
From now on we will say that it boss
is an object of the type Enemy
.
This means that it was created using Enemy
structure as a formula.
Accessing fields
As mentioned above, it boss
contains 3 things (fields), i.e. it consists of three variables.
To get to a specific member of this object, we need to use the following notation:
boss.name = "Ogre";
We use a dot .
to refer to the object's field. In the same way, we can e.g.
modify the enemy's strength:
boss.strength = 50; // I set bosses strength to 50
// Boss enables "fury" skill - strength increases
// Health decreases by a half
boss.strength += 25;
boss.health *= 0.5f;
... or display information about it:
#include <iostream>
#include <string>
struct Enemy
{
std::string name;
float health;
float strength;
};
int main()
{
// I create boss object
Enemy boss;
// I assign values to its fields
boss.name = "Ogre";
boss.health = 250;
boss.strength = 50;
std::cout << boss.name << " has "
<< boss.health << " hp and "
<< boss.strength << " strength."
<< std::endl;
}
Passing objects to functions
Nothing prevents you from creating a function that takes an object of a certain structure as a parameter. A good example will be just displaying information about an enemy:
void printEnemyInfo(Enemy enemy)
{
std::cout << enemy.name << " has "
<< enemy.health << " hp and "
<< enemy.strength << " strength."
<< std::endl;
}
printEnemyInfo
requires a type Enemy
to exist before the function itself is defined.
This means we need to put the function underneath the structure creation (see example below).
Using the above information, we will create a "game" that will have two opponents:
ordinary enemy 👹:
Goblin warrior,60
health,14
strengthboss 💀:
Ogre,250
health,50
strength
#include <iostream>
#include <string>
// Creating struct
struct Enemy
{
std::string name;
float health;
float strength;
};
// Function that display information about an enemy
void printEnemyInfo(Enemy enemy)
{
std::cout << enemy.name << " has "
<< enemy.health << " hp and "
<< enemy.strength << " strength."
<< std::endl;
}
// The main function
int main()
{
// I create boss and goblin objects
Enemy boss;
Enemy goblin;
// I setup goblin object fields
goblin.name = "Goblin warrior";
goblin.health = 60;
goblin.strength = 14;
// I setup boss object fields
boss.name = "Ogre";
boss.health = 250;
boss.strength = 50;
// and print information about them
printEnemyInfo(goblin);
printEnemyInfo(boss);
}
Objects inside arrays
We can put objects inside arrays just like normal variables:
std::vector< Enemy > enemies;
Below is an example of how to add to such an array:
// ...
int main()
{
std::vector< Enemy > enemies;
// (optional)
// A code block to limit the scope
// of local variables inside
{
// I create goblin variable 👉 locally 👈
Enemy goblin;
// I setup the fields
goblin.name = "Goblin warrior";
goblin.health = 60;
goblin.strength = 14;
// I add the object to the vector
enemies.push_back( goblin );
}
// 👈 from this moment, goblin exists ONLY inside the vector
// Print every enemy in the vector
for (Enemy enemy : enemies)
printEnemyInfo(enemy);
}
After reading this lesson, review this sample program: 👾 Battle Arena and its overview. There you will see how arrays and structures are used in practice.
Default field values
We can give the structure elements default values, so we won't have to fill them in every time.
A good example of using the default value is a variable that stores the total amount of damage
an opponent has dealt. To start with, for each enemy, this value will have to be equal 0
.
If you leave the structure field with no default value , e.g .:
struct Car
{
int numberOfWheels;
};
it doesn't mean numberOfWheels
will be set to 0
at the moment of object creation,
you have to do it manually!
To assign a default value to a structure field, we use the usual initialization, known for creating variables:
// Creating the structure
struct Enemy
{
std::string name;
float health;
float strength;
float totalDamage = 0;
};
Now when we create some enemy:
Enemy snake; // snake as an example
then the value of its field totalDamage
snake.totalDamage
will be set to 0
automatically.
You can find out about it, e.g. by printing it:
int main() {
Enemy snake;
snake.name = "Snake";
// 🟡 Note, I'm not setting "totalDamage" manually
std::cout << snake.name
<< " has dealt "
<< snake.totalDamage
<< " total damage";
}
Snake has dealt 0 total damage
Potential errors
This section requires improvement. You can help by editing this doc page.
Here is a list of common errors related to this lesson:
No semicolon after definition
Invalid order
Make sure the structure is defined before using it for the first time.
Wrong code example:
// ❌ Error: usage of type "Enemy" before its definition
void printEnemyInfo(Enemy enemy)
{
std::cout << enemy.name << " has "
<< enemy.health << " hp and "
<< enemy.strength << " strength."
<< std::endl;
}
// Creating the structure
struct Enemy
{
std::string name;
float health;
float strength;
};
This problem can be solved in a different, more convenient way than moving the function
printEnemyInfo
below the structure definition, with the so-called forward declaration.
We will mention it later in the course.
Modifying inside structure definition
Variables cannot be modified inside a structure definition. It is only possible to assign an initial value:
struct Enemy
{
std::string name;
float health;
float strength;
int totalDamage = 0; // OK ✅
// ❌ Error: Attempt to modify field in a wrong place
health = 250;
};