C++: Structures, Unions, Enums and typedef
Group related data with structs, share memory efficiently with unions, name values with enums, and simplify type names with typedef.
Defining and Declaring Structures
A structure groups variables of different types under one name. Each variable inside is called a member. Structures let you model real-world entities like a student, a point, or a product as a single unit instead of scattered individual variables.
struct
A struct is a user-defined type. Once defined, you can declare variables of that type just like int or double.
- Define with the struct keyword followed by a name and a brace-enclosed member list
- Members can be any type: int, double, string, arrays, or other structs
- In C++ (unlike C) you do not need typedef to omit the struct keyword when declaring variables
- Members are public by default in a struct (unlike class where they are private)
Defining and Declaring a Struct
C++Define Student once, then create variables of that type.
Accessing Structure Members
Use the dot operator . to access members of a struct variable. Members can be read and written just like ordinary variables.
Dot Operator (.)
variable.member reads or writes that field directly on the struct object.
- Read: cout << s.name;
- Write: s.age = 21;
- You can assign one struct variable to another: s2 = s1; copies all members
- Structs can be compared member-by-member but not with == by default (unlike std::string)
Accessing and Copying Struct Members
C++Write members with dot notation, then copy the whole struct with assignment.
Nested Structures
A struct can contain another struct as a member. Access nested members by chaining dot operators: outer.inner.field.
Nested Structs
Nest structs to model hierarchical data cleanly instead of duplicating field names across multiple flat structs.
- The inner struct type must be defined before the outer struct uses it
- Access: employee.address.city
- You can initialize nested structs with nested brace lists: {{"Main St", "NY"}}
Nested Structures
C++Access the inner struct's members by chaining two dot operators.
Array of Structures
You can create an array where every element is a struct. This is the natural way to store a collection of similar records, such as a list of students or a catalog of products.
Array of Structs
An array of structs stores records contiguously in memory, making iteration fast and straightforward.
- Syntax: StructType arr[size];
- Access member of element i: arr[i].member
- Initialize with a brace list of brace lists: {{...}, {...}, {...}}
- Use a for loop to process all records
Array of Structs
C++Store three Product records and print each with a for loop.
Pointers to Structures
You can point to a struct with a pointer. Use the arrow operator -> to access members through a pointer instead of writing (*ptr).member.
Arrow Operator (->)
ptr->member is shorthand for (*ptr).member. Always prefer the arrow operator with struct pointers for readability.
- Declare: Student* p = &s;
- Access: p->name is the same as (*p).name
- Pointers to structs are essential when passing large structs to functions without copying
- Dynamic allocation: Student* p = new Student{"Eve", 22, 3.9};
Pointer to Struct with ->
C++Pass a struct by pointer and access its members with the arrow operator.
Structures and Functions
Structs can be passed to and returned from functions. Pass by value copies all members; pass by reference or const reference avoids the copy. Functions can also return a struct to bundle multiple values into one return.
Structs in Functions
Returning a struct is the clean C++ alternative to output parameters when a function needs to produce more than one value.
- Pass by value: void func(Student s), copies all members (expensive for large structs)
- Pass by const reference: void func(const Student& s), no copy, read-only
- Pass by reference: void func(Student& s), no copy, can modify
- Return by value: Student createStudent() { return {"Ali", 19, 3.5}; }
Returning a Struct from a Function
C++Bundle min and max into one struct instead of using output parameters.
Defining Unions
A union is like a struct, but all members share the same block of memory. The union is only large enough to hold its largest member. Only one member can hold a valid value at any given time.
union
Use unions to save memory when a value can be one of several types but only one type is active at a time.
- Syntax matches struct: union Name { members; };
- All members start at the same memory address
- Size of the union equals the size of its largest member (plus any padding)
- Writing to one member and reading a different one is implementation-defined
Union Basics
C++All three members share the same 4 bytes; only the last-written one is valid.
Union vs Structure
The fundamental difference is memory layout. A struct allocates separate memory for every member; a union overlays all members on the same memory. The tradeoff is memory savings versus the ability to hold all members simultaneously.
Union vs Struct
Structs are for objects with multiple attributes active at once; unions are for values that can be interpreted as different types but only one type at a time.
- Struct memory: sum of all member sizes (plus padding)
- Union memory: size of the largest member only
- Structs can safely hold all members simultaneously; unions cannot
- Tagged unions (a union + an enum tracking the active member) are common in practice
| Feature | struct | union |
|---|---|---|
| Memory layout | Each member has its own address | All members share one address |
| Size | Sum of all members (+ padding) | Size of largest member |
| Active members | All members valid at once | Only one member valid at a time |
| Use case | Grouping related attributes | Type-punning, memory saving, variant types |
Anonymous Unions
An anonymous union has no name. Its members are directly accessible in the surrounding scope without needing the union variable name as a prefix. They are often embedded inside a struct to give a field multiple type interpretations.
Anonymous Union
Anonymous unions let you access members directly without a variable name, but they cannot have member functions or private/protected members.
- Declared without a tag name: union { int i; float f; };
- Members are accessed directly: i = 10; not u.i = 10;
- At namespace scope, anonymous unions must be static
- Useful inside a struct to give one field multiple type views
Anonymous Union
C++Members are accessed directly without a union variable name.
Enumerations (enum)
An enum defines a set of named integer constants. Instead of using magic numbers like 0, 1, 2 for days of the week, you write MON, TUE, WED, making code self-documenting.
enum
By default, the first enumerator is 0 and each subsequent one is one more. You can assign custom values.
- Syntax: enum Name { VAL1, VAL2, VAL3 };
- VAL1 = 0, VAL2 = 1, VAL3 = 2 by default
- Custom values: enum Status { OK = 200, NOT_FOUND = 404 };
- Plain enums leak their names into the enclosing scope (can cause name clashes)
Plain enum
C++Named constants replace magic numbers and make switch/if conditions readable.
enum class (C++11)
enum class (scoped enum) fixes the two main problems of plain enums: enumerators are scoped to the enum name and they do not implicitly convert to int.
enum class
Prefer enum class over plain enum in all new C++ code. It prevents accidental name clashes and implicit numeric conversions.
- Syntax: enum class Name { VAL1, VAL2 };
- Access with scope resolution: Name::VAL1
- Cannot compare directly with int without a cast
- Two different enum classes can have enumerators with the same name without conflict
enum class
C++Enumerators are scoped and type-safe; an explicit cast is needed to get the integer value.
typedef and Type Aliases
typedef creates an alternative name for an existing type. C++11 introduced the cleaner using syntax for the same purpose. Both reduce verbosity when working with complex type names.
typedef and using
Prefer the using syntax in modern C++ as it is more readable and supports templates; typedef is still widely found in older codebases.
- typedef syntax: typedef existingType aliasName;
- using syntax: using aliasName = existingType;
- Common use: simplifying long type names like unsigned long long
- Also used to give struct types shorter names in C-style code
typedef and using Aliases
C++Shorten unsigned long long and give a struct a concise alias.
Knowledge Check
1. How do you access a structure member through a pointer?
2. What is the key difference between a struct and a union?
3. How much memory does a union occupy?
4. What does enum class (C++11) add over a plain enum?
5. What does typedef do?
6. Which statement correctly defines a nested structure?
7. What happens when you write to one member of a union and then read a different member?