Structs, Enums, and Type Aliases

Defining and Using Structures

A program rarely works with single numbers or single words; usually, related values have to stick together, and a structure makes it possible for the program to treat them as one unit with a single name.

A structure is written by giving a list of types and names of members inside { }. Note that a structure definition creates no variables: it merely announces a new type that can then be used to declare variables.

#include <iostream>

// Define the members that belong to this type
struct Point
{
    int x;
    int y;
};

int main()
{
    Point p;
    p.x = 10;
    p.y = 20;

    std::cout << p.x << '\n';
    std::cout << p.y << '\n';

    return 0;
}

Declaring Point specifies that objects of type Point will contain two integers called x and y. In main, the variable p is declared as a Point object. This at once sets aside storage for both members, but the members will contain undefined values.

The expression p.x invokes the dot operator. p, on the left of the dot, indicates a specific Point object. x, on the right, names a member that was declared as part of the Point type. The effect of the dot operator is to retrieve the specific x that belongs to a specific object, p. Therefore, p.x = 10; copies the value 10 into the x member of p and no other member.

The expression p.y is similar and accesses the y member within the specific Point object p.

When these are printed using std::cout, the same selections occur, so the program prints 10 and then 20.

The left side of the dot operator can never be a type name; it must always be a specific variable of that type, or some other expression that identifies a specific object. The right side of the dot operator can be any member identifier that is part of that object's type. The entire resulting expression is an ordinary integer that can then be used like any other integer. It can be placed on the right of a = to copy a value, or on the left. It can also be placed on the right of a = to assign the value to a variable, or passed to a function that expects an integer.

You can value the members of an object when you create it using brace initialization.

#include <iostream>

struct Point
{
    int x;
    int y;
};

int main()
{
    Point p { 3, 4 };

    std::cout << p.x << '\n';
    std::cout << p.y << '\n';

    return 0;
}

The initializer { 3, 4 } sets the x member of p to 3, and the y member to 4. The resulting object p now has specified contents. As before, p.x and p.y select the individual members of p. An empty brace initializer {} would initialize each integer member of the structure to 0, while omitting the brace initializer at all would lead to undefined behavior since it would leave each member with undefined contents.

One whole structure can be copied from another by simple assignment.

#include <iostream>

struct Point
{
    int x;
    int y;
};

int main()
{
    Point start { 1, 2 };
    Point end { start };

    end.x = 5;

    std::cout << start.x << '\n';
    std::cout << end.x << '\n';

    return 0;
}

The object end is initialized by making a copy of the object start. Both objects then contain the same values initially, and are independent otherwise. The assignment end.x = 5 copies the value 5 only to the x member of end; the x member of start is still 1. The output of the two prints is therefore 1 and 5.

Structures can be passed to functions by value. This means that a copy of the passed structure is made, which the function then works with on its own.

#include <iostream>

struct Point
{
    int x;
    int y;
};

void printPoint(Point pt)
{
    std::cout << pt.x << '\n';
    std::cout << pt.y << '\n';
}

int main()
{
    Point p { 8, 9 };
    printPoint(p);

    return 0;
}

When the program calls printPoint(p), a copy of p is made and bound to the name pt. Inside the function, the references pt.x and pt.y are actually referring to the x and y members of the local copy pt and not the elements in the original object p. This copy mechanism costs time, proportional to the number of members in the structure, but in many cases the cost will be negligibly small.

Structures can contain members which are structures of other types. To reach a member of a nested structure, you just apply the dot operator again.

#include <iostream>

struct Point
{
    int x;
    int y;
};

struct Segment
{
    Point start;
    Point end;
};

int main()
{
    Segment s { { 0, 0 }, { 5, 5 } };

    std::cout << s.start.x << '\n';
    std::cout << s.end.y << '\n';

    return 0;
}

The object s contains two Point structures named start and end. The expression s.start.x will first look for the member named start within s, which is itself a Point, and then extract the member named x from that Point object s.start. Similarly, the expression s.end.y is evaluated.

Exercise: Defining and Using StructuresStarter code, hints and solutionOpenClose
Guided practiceBeginner · 10–15 min

Practice: Defining and Using Structures

Work through the mission, reveal hints only when needed, and compare your result with the model solution.

Your challenge

Create another Segment with start (1,2) and end (8,9). Print other.start.x and other.end.y using their full member paths.

Mission checklist

  1. Run the starter as a baseline, then predict the change requested in Your challenge.
  2. Make that change in your editor; use the lesson-specific hints if you need help.
  3. Compile and run your answer, then compare with the worked solution and explain the result.

Starter program

This is the working baseline, not the completed answer. Compile as C++20, then make the change described above.

#include <iostream>

struct Point
{
    int x;
    int y;
};

struct Segment
{
    Point start;
    Point end;
};

int main()
{
    Segment s { { 0, 0 }, { 5, 5 } };

    std::cout << s.start.x << '\n';
    std::cout << s.end.y << '\n';

    return 0;
}

Hints

Hint 1

Segment contains two Point members. The first inner brace pair initializes start.x and start.y; the second initializes end.x and end.y.

Hint 2

Create another Segment with two fully initialized endpoints. Use the complete member path, such as other.end.y, to identify exactly which coordinate is printed.

Worked solution

A second Segment other is fully initialized with start (1,2) and end (8,9). The two selected member paths print 1 and 9; the original Segment is unaffected.

#include <iostream>

struct Point
{
    int x;
    int y;
};

struct Segment
{
    Point start;
    Point end;
};

int main()
{
    Segment s { { 0, 0 }, { 5, 5 } };

    std::cout << s.start.x << '\n';
    std::cout << s.end.y << '\n';

    Segment other{{1, 2}, {8, 9}};
    std::cout << "Other endpoints: " << other.start.x << ' ' << other.end.y << '\n';
    return 0;
}

Example output

0
5
Other endpoints: 1 9

0 comments

No comments yet. Be the first.

Add a comment

About CppLearn

CppLearn is an interactive, free reference for modern C++. In each lesson, you'll find out how the language works, rather than just lines of code that you can copy and paste. We cover concepts starting from beginner lessons on fundamentals and proceed to templates, move semantics, smart pointers, and areas that usually trap or scare C++ learners.