Coming Soon

This lesson is currently being developed

Struct miscellany

Additional struct features and best practices.

Compound Types: Enums and Structs
Chapter
Beginner
Difficulty
30min
Estimated Time

What to Expect

Comprehensive explanations with practical examples

Interactive coding exercises to practice concepts

Knowledge quiz to test your understanding

Step-by-step guidance for beginners

Development Status

In Progress

Content is being carefully crafted to provide the best learning experience

Preview

Early Preview Content

This content is still being developed and may change before publication.

13.11 — Struct miscellany

In this lesson, you'll learn about various additional features and considerations when working with structs, including nested structs, struct size and alignment, temporary objects, and other important topics that round out your understanding of structures.

Nested structs

You can define structs inside other structs, creating nested structs. This is useful for organizing related data hierarchically:

#include <iostream>
#include <string>

struct Address
{
    std::string street;
    std::string city;
    std::string state;
    int zipCode;
};

struct Employee
{
    int id;
    std::string name;
    Address homeAddress;    // Nested struct
    Address workAddress;    // Another nested struct
    double salary;
};

int main()
{
    Employee alice{
        1001,
        "Alice Johnson",
        {"123 Oak Street", "Springfield", "IL", 62701},      // homeAddress
        {"456 Corporate Blvd", "Springfield", "IL", 62702},  // workAddress
        75000.0
    };
    
    std::cout << "Employee: " << alice.name << std::endl;
    std::cout << "Home: " << alice.homeAddress.street << ", " 
              << alice.homeAddress.city << ", " << alice.homeAddress.state 
              << " " << alice.homeAddress.zipCode << std::endl;
    std::cout << "Work: " << alice.workAddress.street << ", " 
              << alice.workAddress.city << ", " << alice.workAddress.state 
              << " " << alice.workAddress.zipCode << std::endl;
    
    return 0;
}

Output:

Employee: Alice Johnson
Home: 123 Oak Street, Springfield, IL 62701
Work: 456 Corporate Blvd, Springfield, IL 62702

Structs containing arrays

Structs can contain arrays as members, which is useful for fixed-size collections:

#include <iostream>
#include <string>

struct Student
{
    std::string name;
    int testScores[5];  // Array of 5 test scores
    int numTests;       // Keep track of how many tests are recorded
};

void addTestScore(Student& student, int score)
{
    if (student.numTests < 5)
    {
        student.testScores[student.numTests] = score;
        student.numTests++;
    }
    else
    {
        std::cout << "Cannot add more test scores (maximum 5)" << std::endl;
    }
}

double calculateAverage(const Student& student)
{
    if (student.numTests == 0) return 0.0;
    
    int sum = 0;
    for (int i = 0; i < student.numTests; ++i)
    {
        sum += student.testScores[i];
    }
    return static_cast<double>(sum) / student.numTests;
}

void printStudent(const Student& student)
{
    std::cout << "Student: " << student.name << std::endl;
    std::cout << "Test scores: ";
    for (int i = 0; i < student.numTests; ++i)
    {
        std::cout << student.testScores[i];
        if (i < student.numTests - 1) std::cout << ", ";
    }
    std::cout << std::endl;
    std::cout << "Average: " << calculateAverage(student) << std::endl;
}

int main()
{
    Student bob{"Bob Smith", {}, 0};  // Initialize with empty scores
    
    addTestScore(bob, 85);
    addTestScore(bob, 92);
    addTestScore(bob, 78);
    addTestScore(bob, 88);
    addTestScore(bob, 90);
    
    printStudent(bob);
    
    return 0;
}

Output:

Student: Bob Smith
Test scores: 85, 92, 78, 88, 90
Average: 86.6

Struct size and memory layout

Structs in memory may not be laid out exactly as you might expect due to alignment requirements:

#include <iostream>

struct Example1
{
    char a;      // 1 byte
    int b;       // 4 bytes
    char c;      // 1 byte
};

struct Example2
{
    char a;      // 1 byte
    char c;      // 1 byte
    int b;       // 4 bytes
};

struct Example3
{
    int b;       // 4 bytes
    char a;      // 1 byte
    char c;      // 1 byte
};

int main()
{
    std::cout << "Size of Example1: " << sizeof(Example1) << " bytes" << std::endl;
    std::cout << "Size of Example2: " << sizeof(Example2) << " bytes" << std::endl;
    std::cout << "Size of Example3: " << sizeof(Example3) << " bytes" << std::endl;
    
    std::cout << std::endl;
    std::cout << "Size of char: " << sizeof(char) << " bytes" << std::endl;
    std::cout << "Size of int: " << sizeof(int) << " bytes" << std::endl;
    
    return 0;
}

Output (typical):

Size of Example1: 12 bytes
Size of Example2: 8 bytes
Size of Example3: 8 bytes
Size of char: 1 bytes
Size of int: 4 bytes

The compiler adds padding between members to ensure proper alignment. Example2 and Example3 are smaller because the char members can be packed together more efficiently.

Anonymous structs (implementation-defined)

Some compilers support anonymous structs - structs without names that are embedded directly:

#include <iostream>

struct Point3D
{
    union  // Anonymous union
    {
        struct  // Anonymous struct
        {
            double x, y, z;
        };
        double coords[3];  // Alternative way to access the same data
    };
};

int main()
{
    Point3D p{1.0, 2.0, 3.0};
    
    // Access via named members
    std::cout << "Point: (" << p.x << ", " << p.y << ", " << p.z << ")" << std::endl;
    
    // Access via array
    std::cout << "Coordinates: ";
    for (int i = 0; i < 3; ++i)
    {
        std::cout << p.coords[i];
        if (i < 2) std::cout << ", ";
    }
    std::cout << std::endl;
    
    return 0;
}

Output:

Point: (1, 2, 3)
Coordinates: 1, 2, 3

Note: Anonymous structs are not part of standard C++ and may not work on all compilers.

Temporary struct objects

You can create temporary struct objects that exist only for the duration of an expression:

#include <iostream>

struct Rectangle
{
    double width{0.0};
    double height{0.0};
};

double calculateArea(const Rectangle& rect)
{
    return rect.width * rect.height;
}

void printRectangle(const Rectangle& rect)
{
    std::cout << "Rectangle: " << rect.width << "x" << rect.height 
              << ", Area: " << calculateArea(rect) << std::endl;
}

Rectangle combineRectangles(const Rectangle& r1, const Rectangle& r2)
{
    return Rectangle{r1.width + r2.width, r1.height + r2.height};
}

int main()
{
    // Create temporary Rectangle objects
    printRectangle({5.0, 3.0});  // Temporary Rectangle
    
    double area = calculateArea({4.0, 6.0});  // Another temporary Rectangle
    std::cout << "Area of 4x6 rectangle: " << area << std::endl;
    
    // Use temporary objects in calculations
    Rectangle combined = combineRectangles({2.0, 3.0}, {4.0, 5.0});
    printRectangle(combined);
    
    return 0;
}

Output:

Rectangle: 5x3, Area: 15
Area of 4x6 rectangle: 24
Rectangle: 6x8, Area: 48

Structs and const

You can make struct objects const, and you need to be careful about which operations are allowed:

#include <iostream>
#include <string>

struct Book
{
    std::string title;
    std::string author;
    int pages;
    bool isRead;
};

void markAsRead(Book& book)  // Non-const reference required to modify
{
    book.isRead = true;
}

void displayBook(const Book& book)  // Can accept const references
{
    std::cout << "\"" << book.title << "\" by " << book.author 
              << " (" << book.pages << " pages)"
              << (book.isRead ? " [READ]" : " [UNREAD]") << std::endl;
}

int main()
{
    Book myBook{"The C++ Programming Language", "Bjarne Stroustrup", 1376, false};
    const Book referenceBook{"Effective C++", "Scott Meyers", 297, true};
    
    displayBook(myBook);
    displayBook(referenceBook);
    
    markAsRead(myBook);  // OK: myBook is not const
    // markAsRead(referenceBook);  // ERROR: referenceBook is const
    
    std::cout << "After reading:" << std::endl;
    displayBook(myBook);
    displayBook(referenceBook);
    
    return 0;
}

Output:

"The C++ Programming Language" by Bjarne Stroustrup (1376 pages) [UNREAD]
"Effective C++" by Scott Meyers (297 pages) [READ]
After reading:
"The C++ Programming Language" by Bjarne Stroustrup (1376 pages) [READ]
"Effective C++" by Scott Meyers (297 pages) [READ]

Structs with member functions (preview)

While we haven't covered classes yet, it's worth knowing that structs in C++ can have member functions:

#include <iostream>

struct Circle
{
    double radius{1.0};
    
    // Member function to calculate area
    double getArea() const
    {
        return 3.14159 * radius * radius;
    }
    
    // Member function to calculate circumference
    double getCircumference() const
    {
        return 2 * 3.14159 * radius;
    }
    
    // Member function to modify the circle
    void setRadius(double r)
    {
        if (r > 0)
            radius = r;
    }
};

int main()
{
    Circle myCircle{5.0};
    
    std::cout << "Circle with radius " << myCircle.radius << std::endl;
    std::cout << "Area: " << myCircle.getArea() << std::endl;
    std::cout << "Circumference: " << myCircle.getCircumference() << std::endl;
    
    myCircle.setRadius(3.0);
    std::cout << "\nAfter changing radius to 3:" << std::endl;
    std::cout << "Area: " << myCircle.getArea() << std::endl;
    std::cout << "Circumference: " << myCircle.getCircumference() << std::endl;
    
    return 0;
}

Output:

Circle with radius 5
Area: 78.5397
Circumference: 31.4159

After changing radius to 3:
Area: 28.2743
Circumference: 18.8496

Comparing structs

C++ doesn't provide automatic comparison operators for structs. You need to compare members individually:

#include <iostream>
#include <string>

struct Person
{
    std::string firstName;
    std::string lastName;
    int age;
};

bool areEqual(const Person& p1, const Person& p2)
{
    return (p1.firstName == p2.firstName &&
            p1.lastName == p2.lastName &&
            p1.age == p2.age);
}

bool isOlder(const Person& p1, const Person& p2)
{
    return p1.age > p2.age;
}

int main()
{
    Person alice{"Alice", "Johnson", 25};
    Person bob{"Bob", "Smith", 30};
    Person aliceClone{"Alice", "Johnson", 25};
    
    std::cout << "Alice and Bob are " 
              << (areEqual(alice, bob) ? "equal" : "not equal") << std::endl;
    std::cout << "Alice and Alice clone are " 
              << (areEqual(alice, aliceClone) ? "equal" : "not equal") << std::endl;
    
    std::cout << "Bob is " 
              << (isOlder(bob, alice) ? "older than" : "not older than") 
              << " Alice" << std::endl;
    
    return 0;
}

Output:

Alice and Bob are not equal
Alice and Alice clone are equal
Bob is older than Alice

Structs and arrays

You can create arrays of structs and work with them like any other array:

#include <iostream>
#include <string>

struct Car
{
    std::string make;
    std::string model;
    int year;
    double price;
};

void printCar(const Car& car, int index)
{
    std::cout << index + 1 << ". " << car.year << " " << car.make 
              << " " << car.model << " - $" << car.price << std::endl;
}

Car findCheapest(const Car cars[], int size)
{
    Car cheapest = cars[0];
    for (int i = 1; i < size; ++i)
    {
        if (cars[i].price < cheapest.price)
        {
            cheapest = cars[i];
        }
    }
    return cheapest;
}

int main()
{
    Car inventory[4] = {
        {"Toyota", "Camry", 2022, 28500.0},
        {"Honda", "Civic", 2023, 25200.0},
        {"Ford", "Mustang", 2022, 35000.0},
        {"Nissan", "Altima", 2023, 26800.0}
    };
    
    std::cout << "Car Inventory:" << std::endl;
    for (int i = 0; i < 4; ++i)
    {
        printCar(inventory[i], i);
    }
    
    Car cheapest = findCheapest(inventory, 4);
    std::cout << "\nCheapest car: " << cheapest.year << " " 
              << cheapest.make << " " << cheapest.model 
              << " at $" << cheapest.price << std::endl;
    
    return 0;
}

Output:

Car Inventory:
1. 2022 Toyota Camry - $28500
2. 2023 Honda Civic - $25200
3. 2022 Ford Mustang - $35000
4. 2023 Nissan Altima - $26800

Cheapest car: 2023 Honda Civic at $25200

Best practices and common patterns

1. Organize related data together

// Good: related data grouped logically
struct GameState
{
    int playerScore;
    int playerLives;
    int currentLevel;
    bool isPaused;
};

// Less ideal: scattered individual variables
// int score, lives, level;
// bool paused;

2. Use meaningful names

// Good: clear, descriptive names
struct DatabaseConnection
{
    std::string serverAddress;
    int portNumber;
    std::string databaseName;
    bool isConnected;
};

// Avoid: cryptic abbreviations
struct DbConn
{
    std::string addr;
    int prt;
    std::string db;
    bool conn;
};

3. Consider default values

struct ServerSettings
{
    int maxConnections{1000};
    int timeoutSeconds{30};
    bool enableLogging{true};
    std::string logFile{"server.log"};
};

4. Group related functionality

struct Timer
{
    double startTime{0.0};
    double currentTime{0.0};
    bool isRunning{false};
    
    void start() { /* implementation */ }
    void stop() { /* implementation */ }
    void reset() { /* implementation */ }
    double getElapsed() const { /* implementation */ }
};

Common mistakes to avoid

1. Forgetting struct definition semicolon

struct Point
{
    int x, y;
}  // ERROR: Missing semicolon!

int main() { return 0; }

2. Assuming structs are automatically initialized

struct Data
{
    int value;  // Not automatically initialized to 0!
};

int main()
{
    Data d;  // d.value contains garbage
    // Should use: Data d{} or Data d{0};
    return 0;
}

3. Modifying const struct members

void processData(const DataStruct& data)
{
    // data.someField = newValue;  // ERROR: data is const
    // Must pass by non-const reference to modify
}

Key concepts to remember

  1. Structs can contain other structs (nested structures) for hierarchical data organization.

  2. Memory layout and size may include padding for alignment, affecting the total size.

  3. Temporary struct objects can be created inline for function calls and expressions.

  4. Const structs cannot be modified, but can be read through const-compatible operations.

  5. Structs can have member functions (preview of class-like behavior).

  6. Arrays of structs work like arrays of any other type.

  7. Comparison must be implemented manually - structs don't automatically support == or != operators.

Summary

This lesson covered various additional aspects of working with structs in C++. Understanding nested structs helps you organize complex data hierarchically, while awareness of memory layout and alignment helps you write more efficient code. Temporary objects provide flexibility in function calls and expressions. These miscellaneous features, combined with const-correctness and proper naming conventions, enable you to use structs effectively in a wide variety of programming scenarios. As you progress, you'll see that many of these concepts apply to classes as well, making structs an excellent foundation for understanding object-oriented programming concepts.

Quiz

  1. What are nested structs and when might you use them?
  2. Why might a struct's size in memory be larger than the sum of its member sizes?
  3. How do you create and use temporary struct objects in function calls?
  4. What restrictions apply when working with const struct objects?
  5. How do you compare two struct objects for equality?

Practice exercises

Try these exercises with various struct features:

  1. Create a nested struct system for a library (Book contains Author, Library contains multiple Books) and practice accessing nested members.
  2. Experiment with struct size and alignment by creating structs with different member orders and using sizeof() to observe the differences.
  3. Create a program that uses temporary struct objects in calculations (e.g., mathematical operations with Point or Vector structs).
  4. Design a simple inventory system using an array of Product structs, with functions to search, sort, and modify the inventory.

Continue Learning

Explore other available lessons while this one is being prepared.

View Course

Explore More Courses

Discover other available courses while this lesson is being prepared.

Browse Courses

Lesson Discussion

Share your thoughts and questions

💬

No comments yet. Be the first to share your thoughts!

Sign in to join the discussion