Nested structures

A structure defined inside another structure is called a nested structure. It allows us to organize related data and set a hierarchy in the code format, reducing the complexity of the code and making it easier to understand.

Defining structures inside other structures

To define a nested structure, we can declare the whole structure (Example 1) or a structure variable (Example 2) inside another structure.

#include<iostream>
#include<string>
using namespace std;
struct Person
{
string name;
struct Date {
int day;
int month;
int year;
} dob;
};
Example 1

Example 1: We declare a structure named Date and its structure variable (dob) inside a second structure called Person.

Example 2: We declare the structures Date and Person separately. We only declare a structure variable of the structure Date called dob inside the Person structure.

Accessing members of nested structures

To access the member of a nested structure, we use the . (dot) operator for which we follow this general syntax: outer_struct.inner_struct.member. A working example to portray this concept is given below.

Press + to interact
#include <iostream>
using namespace std;
struct Date
{
int day;
int month;
int year;
};
struct Person
{
string name;
Date dob;
};
int main()
{
Person p = {"John", {30, 5, 1999}};
cout << "Name: " << p.name << endl;
cout << "DOB: " << p.dob.day << "/" << p.dob.month << "/" << p.dob.year << endl;
return 0;
}
  • Lines 4–9: The Date structure is declared, having three int type variables: day, month, and year.

  • Lines 11–15: The Person structure is declared, having a string type name variable and a dob structure variable.

  • Line 19: A p structure variable of the Person structure is declared and initialized, also initializing the dob structure variable of the Date structure inside the curly brackets separated by the comma.

  • Line 20: The string type name variable is printed by using a single . operator functionality.

  • Line 21: The dob structure variable is printed using the double . operator functionality for each member.

An array of structures

Technically speaking, the structure is nothing but a data type. So, like any other data type, we can form an array of structures. Let’s look at how we can create and access an array of the Student structure with two members, name and age, defined below.

struct Student
{
string name;
int age;
};

Defining an array of structures

A declaration of an array of the Student structure will have the following syntax:

Student students[3]{{"John", 23},
{"Jane", 18},
{"Alice", 25}};

A pictorial version of this concept will look like this:

Press + to interact
An array of structures
An array of structures

Accessing individual members of the array

Just like a standard array, to access individual members of an array of structures, we need to access each array element using the index operator. A working example is given below.

Press + to interact
#include <iostream>
using namespace std;
struct Student
{
string name;
int age;
};
int main()
{
//Declaration
Student students[3];
//Separate initialization
students[0] = {"John", 23};
students[1] = {"Jane", 18};
students[2] = {"Alice", 25};
//Accessing members
for (int i = 0; i < 3; i++)
{
cout << "Name: " << students[i].name << endl;
cout << "Age: " << students[i].age << endl;
}
return 0;
}
  • Lines 4–8: The Student structure is declared.

  • Line 13: An array of data type Student of length 3 is declared.

  • Lines 15–17: Array elements are initialized at each index.

  • Lines 19–23: A for loop is used to iterate over the students array to access every individual member using the . (dot) operator.

Passing structures to functions

We can also pass structures to functions. Instead of passing multiple variables, we can send one structure with all these variables as its members. This is probably one of the most helpful functionality structures given to us while writing large codes, making the variable and code management much better. Here’s an example:

Press + to interact
#include <iostream>
using namespace std;
struct Rectangle
{
int length;
int width;
};
double area(Rectangle r)
{
return r.length * r.width;
}
int main()
{
Rectangle r = {2, 5};
cout << "Area of Rectangle: " << area(r) << endl;
return 0;
}
  • Lines 4–8: A structure named Rectangle is declared with length and width as its members.

  • Line 10: The area function is declared, and the r structure variable of the Rectangle structure is given as its parameter.

  • Line 12: The area of the rectangle is calculated by taking the product of the members of the structure variable and is returned.

  • Line 17: An r structure variable is declared and initialized.

  • Line 18: The result of the product (area) is printed.

Using structs to return multiple values

Furthermore, with the utilization of structures, there is no longer a need to pass variables by reference in order to update multiple values using a function (which is restricted in C++ when it comes to returning multiple values). Instead, we can simply return the structure itself. Let’s see an example.

Press + to interact
#include <iostream>
using namespace std;
struct Point2D
{
double x;
double y;
};
Point2D midpoint(Point2D p1, Point2D p2)
{
Point2D m;
m.x = (p1.x + p2.x) / 2.0;
m.y = (p1.y + p2.y) / 2.0;
return m;
}
int main()
{
Point2D p1= {0.0,0.0};
Point2D p2= {1.0,1.0};
Point2D m = midpoint(p1, p2);
cout << "Mid point: ("<<m.x<<","<<m.y<<")"<<endl;
return 0;
}
  • Lines 4–8: A structure named Point2D is declared with coordinates x and y as its members.

  • Line 10: A midpoint function of the type Point2D is declared, which takes two Point2D variables to compute their midpoint. It’s important for the function to have the same return type as the Point2D structure because we have to return a structure variable.

  • Lines 12: An m structure variable is declared, which stores the midpoint.

  • Lines 13–14: The midpoint of the two points is calculated, and the members of the m structure variable are updated.

  • Lines 20–21: Structure variables for two points, p1 and p2, are declared and initialized.

  • Line 22: An m structure variable is declared, and the midpoint() function is called with p1 and p2 as its arguments.

  • Line 23: The members of the m structure variable are accessed and printed.

Structure manipulation with pointers

We’ll now explore how we can use structure manipulation with pointers. Like other data types, we can make a pointer of the structure data type (user-defined data type). We access each member using the -> (arrow) operator. All of the pointer rules we learned in the previous section also apply here.

Let’s look at an example:

Press + to interact
#include <iostream>
using namespace std;
struct Point2D
{
int x;
int y;
};
int main()
{
Point2D* p = new Point2D{}; // This will declare one Point2D object
p->x = 10; // or (*p).x = 10;
(*p).y = 20; // or p->y = 20;
cout << "X cordinate: " << p->x << endl;
cout << "Y cordinate: " << p->y << endl<< endl;
delete p; //Deleting the pointer or freeing the space???
// This is a dangerous code now,
// as this memory is taken away from the program, hence a logical error
cout << "After Deleting Pointer: " << endl;
cout << "Pointer p: " << p << endl;
cout << "X cordinate: " << p->x << endl;
cout << "Y cordinate: " << p->y << endl << endl;
p = new Point2D[3]{{1,1},{2,2}, {}};
cout << "New Pointer Allocation: " << endl << "Points: { ";
for(int i=0; i<3; i++)
{
//All three of the below methods to access array are valid
cout << "("<<p[i].x<<","<<p[i].y<<") ";
cout << "("<<(*(p+i)).x<<","<<(*(p+i)).y<<") ";
cout << "("<<(p+i)->x<<","<<(p+i)->y<<") ";
}
cout << "}";
delete [] p;
return 0;
}
  • Lines 4–8: A structure named Point2D is declared with coordinates x and y as its members.

  • Lines 12–14: A p pointer of the type Point2D is declared, and its members are initialized.

  • Lines 19: The p pointer is deleted, freeing the allocated space.

  • Line 24–25: Members of p are accessed and printed using the -> (arrow) operator.

  • Line 27: The p pointer is now pointed at a new memory, an array of Point2D elements.

  • Lines 29–34: A loop iterates over and accesses each array element to print. Accessing each element is done in three different ways, all of which are the same:

    • Line 32: The .(dot) operator is used to access the array elements.

    • Line 33: The array pointer is dereferenced with its address in the round brackets. Each time i iterates, the address is incremented, and the next array element is printed.

    • Line 34: The -> (arrow) operator is used to access the elements. When doing that, we don’t need to dereference the pointer.

Results

The p pointer is populated with the x and y coordinates using the -> (arrow) operator, but these values are soon set to 0 as we delete the p pointer (deallocating memory), which means that the value at the pointer memory location is now set to Null. Later, we set this pointer to a new array of the type Point2D. Accessing this pointer shows how the p pointer still exists and is reusable.

Here’s a pictorial version of this concept:

Press + to interact
canvasAnimation-image
1 of 11

Passing structures by value and by reference

A structure variable can also be passed by reference to a function, which can be useful for updating its members’ values from within the function. Let’s look at an example to see the difference between the two in the code below:

Press + to interact
#include <iostream>
using namespace std;
struct Point2D
{
int x;
int y;
};
void passByValue(Point2D p)
{
p.x = 20, p.y = 30;
}
void passByReference(Point2D& p)
{
p.x = 20, p.y = 30;
}
void printPoint(Point2D p)
{
cout << "("<<p.x<<","<<p.y<<")"<<endl;
}
int main()
{
Point2D p{}; // initialize p.x = 0, p.y = 0
cout << "Initial Value of p: ";
printPoint(p);
passByValue(p);
cout << "Value of p after passByValue: ";
printPoint(p); // p will be the same
passByReference(p);
cout << "Value of p after passByReference: ";
printPoint(p); // p will be updated
return 0;
}

  • Lines 4–8: A structure named Point2D is declared with coordinates x and y as its members.

  • Lines 10–13: A function named passByValue() is defined, which takes a Point2D variable p passed by value as its parameter, updating the members of p.

  • Lines 15–18: A function named passByReference() is defined, which takes a Point2D variable p passed by reference as its parameter, updating the members of p.

  • Lines 20–23: A helper function named printPoint() is defined, which prints the members of Point2D variables.

  • Line 27: A p structure variable of type Point2D is declared (with members initialized to Null).

  • Lines 31–32: passByValue() is called and p is passed as its argument, and the helper function printPoint() is called to print elements of p.

  • Lines 35–37: passByReference() is called and p is passed as its argument, and the helper function printPoint() is called to print elements of p.

Results

We can see how when the passByValue() function is called, the values of the members of p are still 0, which confirms that when arguments are passed by value, they are not updated at their original addresses. On the other hand, the values of the members are updated at their original addresses when the passByReference() function is called, and the arguments are passed by reference.

That’s all that the structure in C++ entails. Let’s test ourselves to see if we can implement the above techniques in a practical coding example.

Exercise: Division using struct

Write a function that takes two integers (dividend and divisor) as input and returns two values (quotient and remainder) using struct in the coding example given below.

Press + to interact
#include <iostream>
using namespace std;
struct Pair
{
int v1;
int v2;
};
Pair divide(int dividend, int divisor)
{
//Write your code here
//Function should return a structure variable of structure "Pair"
}
int main()
{
Pair P = divide(19, 5);
int quotient = P.v1;
int remainder= P.v2;
cout << "Quotient: "<<quotient<<endl
<< "Remainder: "<<remainder<<endl;
return 0;
}


If you have trouble getting to the solution, you can click the “Show Solution” button to see how to solve the problem.