8.4 — Structs

There are many instances in programming where we need more than one variable in order to represent an object. For example, to represent yourself, you might want to store your name, your birthday, your height, your weight, or any other number of characteristics about yourself. You could do so like this:

However, you now have 6 independent variables that are not grouped in any way. If you wanted to pass information about yourself to a function, you’d have to pass each variable individually. Furthermore, if you wanted to store information about someone else, you’d have to declare 6 more variables for each additional person! As you can see, this can quickly get out of control.

Fortunately, C++ allows us to create our own user-defined aggregate data types. An aggregate data type is a data type that groups multiple individual variables together. One of the simplest aggregate data types is the struct. A struct (short for structure) allows us to group variables of mixed data types together into a single unit.

Declaring and defining structs

Because structs are user-defined, we first have to tell the compiler what our struct looks like before we can begin using it. To do this, we declare our struct using the struct keyword. Here is an example of a struct declaration:

This tells the compiler that we are defining a struct named Employee. The Employee struct contains 3 variables inside of it: an int named id, an int named age, and a double named wage. These variables that are part of the struct are called members (or fields). Keep in mind that Employee is just a declaration -- even though we are telling the compiler that the struct will have member variables, no memory is allocated at this time. By convention, struct names start with a capital letter to distinguish them from variable names.

Warning: One of the easiest mistakes to make in C++ is to forget the semicolon at the end of a struct declaration. This will cause a compiler error on the next line of code. Modern compilers like Visual Studio 2010 will give you an indication that you may have forgotten a semicolon, but older or less sophisticated compilers may not, which can make the actual error hard to find.

In order to use the Employee struct, we simply declare a variable of type Employee:

This defines a variable of type Employee named joe. As with normal variables, defining a struct variable allocates memory for that variable.

It is possible to define multiple variables of the same struct type:

Accessing struct members

When we define a variable such as Employee joe, joe refers to the entire struct (which contains the member variables). In order to access the individual members, we use the member selection operator (which is a period). Here is an example of using the member selection operator to initialize each member variable:

As with normal variables, struct member variables are not initialized, and will typically contain junk. We must initialize them manually.

In the above example, it is very easy to tell which member variables belong to Joe and which belong to Frank. This provides a much higher level of organization than individual variables would. Furthermore, because Joe’s and Frank’s members have the same names, this provides consistency across multiple variables of the same struct type.

Struct member variables act just like normal variables, so it is possible to do normal operations on them:

Initializing structs

Initializing structs by assigning values member by member is a little cumbersome, so C++ supports a faster way to initialize structs using an initializer list. This allows you to initialize some or all the members of a struct at declaration time.

If the initializer list does not contain an initializer for some elements, those elements are initialized to a default value (that generally corresponds to the zero state for that type). In the above example, we see that frank.wage gets default initialized to 0.0 because we did not specify an explicit initialization value for it.

Non-static member initialization

It’s possible to give non-static (normal) struct members a default value:

If both non-static member initializer and list-initialization are provided, the list-initialization takes precedence.

In the above example, Rectangle x would be initialized with length and width 2.0.

We talk about what static members are in a later chapter. For now, don’t worry about them.

Assigning to structs

If we wanted to assign values to the members of structs, we can to do so individually:

If you want to assign a new value to all members, this is a pain, particularly for structs with many members. You can also assign values to structs members using an initializer list:

Structs and functions

A big advantage of using structs over individual variables is that we can pass the entire struct to a function that needs to work with the members:

In the above example, we pass an entire Employee struct to printInformation() (by value, meaning the argument is copied into the parameter). This prevents us from having to pass each variable individually. Furthermore, if we ever decide to add new members to our Employee struct, we will not have to change the function declaration or function call!

The above program outputs:

ID:   14
Age:  32
Wage: 24.15

ID:   15
Age:  28
Wage: 18.27

A function can also return a struct, which is one of the few ways to have a function return multiple variables.

This prints:

The point is zero

Nested structs

Structs can contain other structs. For example:

In this case, if we wanted to know what the CEO’s salary was, we simply use the member selection operator twice: myCompany.CEO.wage;

This selects the CEO member from myCompany, and then selects the wage member from within CEO.

You can use nested initializer lists for nested structs:

Struct size and data structure alignment

Typically, the size of a struct is the sum of the size of all its members, but not always!

Consider the Employee struct, but with fixed-size integers and `id` being half the size of `age`. On many platforms, a double is 8 bytes, so we’d expect Employee to be 2 + 4 + 8 = 14 bytes. To find out the exact size of Employee, we can use the sizeof operator:

On the author’s machine, this prints:

The size of a double is 8
The size of Employee is 16

It turns out, we can only say that the size of a struct will be at least as large as the size of all the variables it contains. But it could be larger! For performance reasons, the compiler will sometimes add gaps into structures (this is called padding).

In the Employee struct above, the compiler is invisibly adding 2 bytes of padding after member id, making the size of the structure 16 bytes instead of 14. The reason it does this is beyond the scope of this tutorial, but readers who want to learn more can read about data structure alignment on Wikipedia. This is optional reading and not required to understand structures or C++!

Accessing structs across multiple files

Because struct declarations do not take any memory, if you want to share a struct declaration across multiple files (so you can instantiate variables of that struct type in multiple files), put the struct declaration in a header file, and #include that header file anywhere you need it.

Struct variables are subject to the same rules as normal variables. Consequently, to make a struct variable accessible across multiple files, you can use the extern keyword in the declaration in the header and define the variable in a source file.

Final notes on structs

Structs are very important in C++, as understanding structs is the first major step towards object-oriented programming! Later on in these tutorials, you’ll learn about another aggregate data type called a class, which is built on top of structs. Understanding structs well will help make the transition to classes that much easier.

The structs introduced in this lesson are sometimes called plain old data structs (or POD structs) since the members are all data (variable) members. In the future (when we discuss classes) we’ll talk about other kinds of members.

Quiz time

Question #1

You are running a website, and you are trying to keep track of how much money you make per day from advertising. Declare an advertising struct that keeps track of how many ads you’ve shown to readers, what percentage of ads were clicked on by users, and how much you earned on average from each ad that was clicked. Read in values for each of these fields from the user. Pass the advertising struct to a function that prints each of the values, and then calculates how much you made for that day (multiply all 3 fields together).

Show Solution

Question #2

Create a struct to hold a fraction. The struct should have an integer numerator and an integer denominator member. Declare 2 fraction variables and read them in from the user. Write a function called multiply that takes both fractions, multiplies them together, and returns the result as a decimal number. You do not need to reduce the fraction to its lowest terms. Print the result of the multiplication of the 2 fraction variables.

Show Solution

8.5 -- Random number generation
8.3 -- Enum classes

611 comments to 8.4 — Structs

  • Mae

    Hello, great tutorial!

    I have a question about struct member variable zero-initialization and nested structs. In the first example under the Nested Struct section you nest `Employee CEO;` inside struct Company and then declare the uninitialized (by lack of empty curly braces) `Company myCompany` variable. When the values of `myCompany` are printed out, only `myCompany.numberOfEmployees` is given a junk value while the nested Employee struct members have zero values. What is the reason for this?

    • nascardriver

      Nothing in that struct is initialized. Accessing uninitialized variables causes undefined behavior. Undefined behavior can mean printing zeroes.

      To be safe, everything should be initialized as always.

      The empty curly braces at `myCompany` alone would be enough to initialize everything, including `CEO`.

  • rmaxx

    when run above quiz 1,2 in my cfree software,it is tell that ad and f1,f2 are not declared.why is that?

  • Gabe

    Are structs mainly used for data structures? For Example: You may have a class that defines operations of that of a Singly Linked List, and have a struct nested inside of it that basically represents a node?

    • nascardriver

      Structs conventionally only store data and don't provide any operations on it. Your example is correct. Though if the nodes are exposed to the outside of the class (eg. by returning a node from a public member function), they shouldn't be a nested type.

  • koe

    Section 'Accessing structs across multiple files' seems like it could use some elaborating. For example, a normal global variable is externally linked by default so is 'extern' really necessary? How does initialization play with using across files, since you have 'non-static member initialization'? Is 'non-static member initialization' impossible in C++11 if you want to make a struct variable externally linked (EDIT: my test says in that case forward declare with 'extern MyStruct struct_name;', then define with 'MyStruct struct_name;')? I assume you must explicitly initialize a struct variable where it is supposed to be defined, and then forward declare it in headers for use elsewhere.

    • nascardriver

      I slightly modified the paragraph to make the "extern" part less confusing.
      As the lesson says, there's no difference between struct variables and other variables. You have to define the variable once and only once by placing it in a source file (With an initializer). The declaration, with `extern`, goes in the header.

  • Gabe

    Quiz 1:

    Quiz 2:

    • nascardriver

      - Initialize variables unless you really want an uninitialized variables.
      - Avoid abbreviations. Name variables descriptively.

  • Shady

    Hey.. this feels very very wrong to use struct like this.. can you give me an idea how to use it better? ...

  • Robbas

    Hi Nascar, is it possible to do something like this?



    It doesn't work. I'd like to share the struct through the namespace but I don't know if it's possible and how to do it.

    • nascardriver

      You can do this with `static` member variables (Covered later). In your example, it doesn't make sense to make the members `static` (ie. accessible as you showed). Michael is clearly in instance and an employee and should be stored in a dedicated variable.
      Until you learn about `static` members, you can use a namespace with regular variables.

  • Dudz

    My answer for question#2

  • Dudz

    My answer for question#1

    • nascardriver

      - Avoid `static` local variables. Your function isn't reusable.
      - Use ++prefix unless you need postfix++. postfix++ is slower.
      - The evaluation order of arguments is unspecified (Line 34). If you need arguments to be evaluated in a specific order, which you do, call the functions and store their return values in variables.

  • Aiden

    Hi, I don't know if this is the right place to post it but I have been working on a snake game and I came upon a weird error. On line 56 an exception is always thrown the second time the loop repeats and I have confirmed that it is a problem with playerInput is it conio.h thats causing the exception? it was working fine until I tried to return a whole struct. Here is the full exception that it spits out "Unhandled exception at 0x00E52664 in SnakeGame.exe: 0xC0000005: Access violation reading location 0x00000000."
    Thank you in advance for responding

  • salah

    ""Because struct declarations do not take any memory, if you want to share a struct declaration across multiple files (so you can instantiate variables of that struct type in multiple files), put the struct declaration in a header file, and #include that header file anywhere you need it.""

    do you mean here that the entire declaration of the structure won't take any memory until we create an object of that structure ??
    is it the same case with classes ??

    • Alex

      Yes. You can think of a type declaration like a blueprint. The blueprint isn't an actual house. It doesn't take any wood to build. It's the actual houses that take wood to build. But the blueprint tells you what the houses that you will build are going to look like.

  • Kristen

    So well written and concise. Thanks so much.

  • hellmet

    I can't seem to separate a struct's member functions in to a .cpp and .hpp files. Is that a limitation?

    like so

    Also, I have this code. It should work as expected, right (no undefined behavior invoked)?

    • nascardriver

      Apart from the missing semicolon after the struct declaration, everything is good.

      No UB in your second snippet. Considering you're returning a const reference to a string literal, I recommend you to use a `std::string_view` instead (No need for const/static/temporaries).

      • hellmet

        How 'bout definition vs declaration of struct member functions? Hmm strange, it didn't compile yesterday saying 'redefinition error'. It compiles okay today *shrug*.

        Ahh string_view. Yes I thought of using it, but wasn't sure of it's semantics. How it would work when I return by value etc... Where do I look these up, the behaviors?


        • nascardriver

          cppreference is the place to go. You're probably interested in the constructors and assignment operator.

          I pass `std::string_view` by value, because I read somewhere that it's faster. The member functions of `std::string_view` also pass by value. I did a benchmark in debug mode without using the view (Just passing it). Reference was faster. That's just in debug mode though, results can be very different in real code in release mode. Do you own benchmark.

          • hellmet

            Hmm I see, god this get's complicated fast, even for seemingly simple things!
            So this is similar to a regular string then, for the most part, in the sense of assignment, copies etc, apart from the fact that it can't be modified.

            Need to get a hang of reading cppreference though, get a hand of making deep conclusions of the spec.

  • James

    For Question 1, I copy and pasted the solution to play around with it, but it returns an error at line 38 saying "cannot convert 'Advertising' to 'int' in initialization". Also rewrote it manually and same error pops up. I'm sure it's something very easy to fix but what's going on here?

  • kavin

    For 2nd example if i put the equation like this , it produces wrong result.

    Since the question is ,"take both fractions and multiply them together" 1st we do division of fractions and then multiply the results. This eqn produces wrong result. why ?

    • nascardriver

      `numerator` and `denominator` are `int`s. You're dividing integers, which produces an integer as a result. You need at least 1 `double` in the division. Cast either the numerator or denominator _before_ you divide.

      • kavin

        Thank you. Do i still have to use "extern" keyword in struct even if i put the struct declaration in a header file, and #include that header file in other files ? (because non-constant globals have external linkage by default and only the declaration in header need extern keyword right?)

        • nascardriver

          `struct`s can be defined multiple times, as long as they're in different source files. You don't need to forward declare them.


          Other files can #include "header.hpp" without problems.

  • Alixsep

    Hi Alex and nascardriver!

    your Quiz 2 has a bug! you should use static_cast for the f2 too!

    How is my attempt?

    I make music too, check them out if you like!
    my latest music video:

    • nascardriver

      `f2` or `b` doesn't need a cast, the calculation is performed left-to-right:

      The solution was confusing nonetheless. I changed it to cast the entire first multiplication, that should make it clearer.

      Your code looks good :)

  • ayush k jain

    hey alex thanks for the tutorials .helping a lot of people out there!

    the code compiles fine but is not working properly
    in this code i have used an algorithm to sort a list of numbers in ascending order

    • nascardriver

      > is not working properly
      What was the input?
      What was the output?
      What was the expected output?
      What did you do to try to locate or fix the problem?

    • Paul McGee

      Suggestion ... keep your code simple and correct, before adding more to it.
      You can use an array, and leave the user input until later when it runs properly.

      (Also you had some typing / logic mistakes ... eg "//" ">" )

  • Ryan

    why is it that

    Prints out 0 and not a floating point number, as we defined value as a type double.

    • nascardriver

      The type of `value` is `double`, but `1` and `16` are integers. You're dividing integers, which produces `0`, then you're converting the `0` to a `double`.

  • Can anyone help!!!
    I just can't think of how to calculate total!!!

    • nascardriver

      Remove line 2-4. `using namespace` can cause name collisions, the variables are supposed to be read from the user.
      Declare `day` in `main` and pass it to `income` as a parameter.
      Once you know how many ads were clicked, you just need to multiply that value with the income per clicked ad.

      • Thanks a lot for the reply!!!
        Sorry of being so dumb or may be this is a language barrier (i'm not an English speaking person), but i still can't get it?
        In a case i've entered 5 days period in my code, how should it work if i:
             "Declare `day` in `main` and pass it to `income` as a parameter."
        nascatdriver, it would be really nice if You could post a piece of code.
        Many thanks in advance!!!

        • nascardriver

          Have a look at the `printInformation` function in the "Structs and functions" section of this lesson. There, an `Employee` is passed to a function.

  • BooGDaaN

    Thanks for this tutorial! What do you think about this application? Your suggestions and advices will helped me.

    • nascardriver


      Your initializations are a little wonky, otherwise it looks alright:

      - Initialized variables with brace initialization (`Person::genre` and `i`).
      - You can use `{}` to initialize variables to their default value. That's not only shorter, but works for all types the same and can also be faster (eg. for `std::string`).
      - Line 47-51: Initialize variables instead of assigning to them.
      - Use ++prefix unless you need postfix++.
      - Line 78: Use single quotation marks for characters ('.').

      `std::string` can be used to repeat a character

      • BooGDaaN

        Thank you very much!
        That "for" structure was automatically generated from VS, so I didn't pay attention. My mistake. I updated the code.

  • Adam

    • nascardriver

      - Use double literals for doubles (100.0 instead of 100, 0.0 instead of 0).

      > Pass the advertising struct to a function
      You used the structs properties individually, which defeats the purpose of the struct. See the quiz's solution.

  • Mike

    In this example from under the section "C++11/14: Non-static member initialization"

    If I use Uniform, the 1st curly says a semicolon is expected. If I use Direct, then I get an error with x.

    Why can't we use Direct or Uniform initialization? Is it because Uniform is expecting two parameters for rectangle (length and width), even though I'm only specifying the x.length?

    • `x.length` is a variable. You can only initialize it when it gets created. It gets created when you create `x`.
      `x.length = 2.0` is an assignment, not an initialization.

      • Mike

        But isn't x created in the first line?

        Or are you saying that once x is created, then you can't recreate it, which is what I'm doing when I use direct or uniform?

  • Andrea

    I'm using VS 2019 and this line gives the C26451 warning.

    "Arithmetic overflow: Using operator '*' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator '*' to avoid overflow (io.2)."

  • Dear Teacher,
    Please let me suggest you add a section with title "Designated Initializers" and text
    "For Visual Studio 2019 users: After you have created "Windows Desktop Wizard" project, go to "Solution Explorer", double click project's name > select Properties > C/C++ > Language > Standard > select the third (last one) option: ... (std:c++latest draft) > OK.
    Copy following program and paste in V.S. code area. Hit [Ctrl] + [F5].

    This program outputs "50000".
    Variable members not initialized in variable, are initialized with default value in struct." Regards.

    • Alex

      Hi Georges. Designated initializers are on my to-do list to cover when I update the lesson. Thanks for the suggestion!

      • Dear Teacher,
        Please accept my many thanks for you replied my message and many more for your thanks. Also let me post here my suggestion corrected.
        "For Visual Studio 2019 users: After you have created "Windows Desktop Wizard" project, go to "Solution Explorer", right click project's name > Properties > C/C++ > Language > Language Standard > down arrow at the end of the field > the third (last one) option: ... (std:c++latest draft) > OK.
        Copy following program and paste in V.S. code area.

        Run program. It outputs
        Variable members not initialized in variable, are initialized with values (designated initializers) in struct."

  • Rob

    In the code under the heading "Structs and functions", we have "\n", i.e. with double quotes, but lesson 1.5 said we needed to use single quotes when we use \n like this. Both ways seem to work fine for me though (VS 2019 community) - is there any difference between "\n" and '\n'? The relevant code is:

    • It produces the same observable outcome, but '\n' allows `std::cout <<` to use an optimized function to print.
      "\n" is a string. Whenever you pass it to a function, that function has to determine the string's length.
      '\n' is a char. It's length is always 1, so there's no need to determine its length.

      Using quotation marks for characters wastes resources.

  • VerticalLimit

    Hi Nascardriver

    Any pointers to make both codes better ?

    Also for Quiz 2, Iam trying to modifying and use tuple instead of void() just so that i can return multiple values and change the code slightly. In a bigger spectrum will this be beneficial ?
    (so far iam getting -nonIND error which you have already covered. Obviously there is a logical issue which I will try to fix but the question is whether it is worthwhile to modify)

    QUIZ 1

    QUIZ 2

    • - Initialize your variables with brace initializers.
      - Use double literals for doubles.
      - Inconsistent formatting. Use your editor's auto-formatting feature.

      Quiz 1
      - Line 17-18 don't do anything.
      - Unused parameter `earned`.
      You didn't do what the quiz asked you to. If the was on purpose, that's fine. If you did this because you don't know how to do it properly, look at the solution and make sure you understand it.

      > In a bigger spectrum will this be beneficial
      I don't see where you'd use an `std::tuple`. Do it anyway just so you learn how to use it.

      • VeritcalLimit

        Hi Nascardriver
        Thanks for your feedback as always very helpful. Yes I do at times (most often) try to intentionally deviate from what quiz asks for so that I can learn what works and what doesn't hence the question about std::tuple. I prefer to look at the Solutions only at very end once I have published the Quiz. Better indication what I can improve on.
        Could you please clarify the following so that I have a better understanding going forward:

        - Line 17-18 don't do anything. I want the user to enter the amount of ads and how many times ads was clicked on ? (in duplicate quiz Iam running few different ads and I want to see % of each)

        - Unused parameter `earned` : Ive declared 'earned' within dayProfit(double earned) function and then again line 35 and 36.

        - Initialize your variables with brace initializers : I would imagine you are referring to line 5 - 8. If it is as is what potential problem I could run into ? (I will adjust the code regardless)

        Thank you

        • > Line 17-18 don't do anything.

          You're doing the same. Remove those lines.

          > Unused parameter `earned`
          You're not using `earned` in `main` and you're not using the value pass to `dayProfit` through `earned`. `earned` should be a local variable. Or return without storing the result in a temporary.

          > Initialize your variables with brace initializers.
          All your declarations would be better with empty brace initializers to guarantee initialization in all situations, but that's not what I was referring to.
          I meant quiz 2 line 38, 39. You're using copy initialization, which isn't as universal and type-safe as brace initialization.

  • Piyush

    Unfortunately, in C++11, the non-static member initialization syntax is incompatible with the initializer list and uniform initialization syntax. For example, in C++11, the following program won’t compile:

    Bro this is compiled in my compiler.

    How ??

    Is this only related to C++11 version?

    I have the latest visual studio and installed last month, did I missed something ?

    Got that bro c++14 lift this shit up! Thanks

  • Sam

    Here is my solution for Question #2. Any feedback would be appreciated :)!

    • - Initialize your variables with brace initializers.
      - `main`: Missing return-statement.
      - Line 10, 11 should be indented.
      - `Numerator_t` and `Denominator_t` should be declared in `Fraction` and used there too. There's no point in aliasing types if you're not using them everywhere.

      • Sam

        Thank you for your continuous feedback :). Here is my updated code. I decided to remove the typedef completely as having it in the struct and using it on the struct members is superfluous. I also removed the typedefs because I switched from having 2 seperate functions (getNumerator, getDenominator) to having 1 function (getFraction) that gets the entire fraction and returns type "Fraction".

  • Sam

    Here is my solution to problem #1. I would appreciate any feedback I can get! :)

    • - Line 25: Initialize your variables with brace initializers. This line is unnecessary altogether.
      - `main`: Missing return-statement. It should be added for consistency.
      - Use double literals for doubles (100.0 instead of 100).
      - You're using the same name style (lower camel case) for variables and functions, this can lead to confusion.

      • Sam

        Once again, thank you for your excellent feedback. Here is my updated code.

        Also, you stated "You're using the same name style (lower camel case) for variables and functions, this can lead to confusion.", so I changed the functions to all lowercase with a _ seperator. I hope this is what you meant.

  • Edgar J. Wurst

    Any comments on question 1:

    • - Line 43, 44: Initialize.
      - Line 27, 28: There's no need for a variable, you can `return { ... }`.
      - Line 16, 20, 24: You don't need those. Create a `Moneymade` at the top of the function and extract directly into it.

      • Edgar J. Wurst

        So like this?

Leave a Comment

Put all code inside code tags: [code]your code here[/code]