Search

S.4.7 — 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: a short 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.

In C++11, we can also use uniform initialization:

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.

C++11/14: Non-static member initialization

Starting with C++11, it’s possible to give non-static (normal) struct members a default value:

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:

Consequently, in C++11, you’ll have to decide whether you want to use non-static member initialization or uniform initialization. Uniform initialization is more flexible, so we recommend sticking with that one.

However, in C++14, this restriction was lifted and both can be used. If both are provided, the initializer list/uniform initialization syntax takes precedence. In the above example, Rectangle x would be initialized with length and width 2.0. In C++14, using both should be preferred, as it allows you to declare a struct with or without initialization parameters and ensure the members are initialized.

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

Assigning to structs

Prior to C++11, if we wanted to assign values to the members of structs, we had to do so individually:

This is a pain, particularly for structs with many members. In C++11, you can now 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. On many platforms, a short is 2 bytes, an int is 4 bytes, and 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 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 to do so.

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

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).

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 prints the result out as a decimal number. You do not need to reduce the fraction to its lowest terms.

Quiz Answers

1) Show Solution

2) Show Solution

S.4.8 -- The auto keyword
Index
S.4.6 -- Typedefs and type aliases

414 comments to S.4.7 — Structs

  • Dear Teacher,
    Please let me say you what I understand and what I do not in your solution for 1st quiz.
    Function "getAdvertising()" of the type "Advertising" returns variable "temp" of same type. But it doesn't have any specific value. Then how does variable "ad" of same type, get value if does? Regards.

  • Dear Teacher,
    Please your comment on my answer to 1st quiz:

    • * Don't use "using namespace".
      * Missing printed trailing line feed (Print a line feed when your program is done).

      "Pass the advertising struct to a function that prints each of the values"
      You didn't do that. Try it out to make sure you understood structs.

  • Napalm

    I think I got it right. For some reason though the console doesn't print a £, it instead prints a u with an accent. Any idea why that happens?

  • Dear Teacher,
    Please let me say that I can not understand that "... called plain old data structs (or POD structs) since the members are all data (variable) members.". Meaning of "old" is different than "all". Regards.

  • Torraturd

    is this good enough?

    • * Line 37: Limit your lines to 80 characters in length for better readability on small displays.
      * Line 24, 30, 33: Use single quotation marks for characters.
      * Line 32: Use double literals for doubles (100.0 instead of 100).
      * Line 37: Missing printed line feed.

      • Torraturd

        Thank you so much!, but I don't understand what you mean by 'Missing printed line feed', could you elaborate a bit?

        • Your program doesn't print a line feed at the very end. This causes the output of the terminal to be on the same line as the last line of your program, which looks ugly.

          Better

  • Nguyen

    Hello,

    Chapter 2.3:
    "When a function is called, all of the parameters of the function are created as variables, and the value of each of the arguments is copied into the matching parameter. This process is called pass by value."

    In this case, add() is called where the value of a is copied into both parameters x and y. Since a has value 5, add(a, a) = add(5, 5), which resolves to value 10.

    Now let's apply what I learned from chapter 2.3 to the example just below Structs and functions section in this chapter.
    I can't really visualize what the value of each of the arguments (joe or frank) is copied into the parameter.

    Below is what I guess in pictorial format when printInformation()is called:

    When printInformation(joe.id, joe.age, joe.wage) is evaluated, function printInformation is called, the value (14) of joe.id, the value (32) of joe.age and the value (24.15) of joe.wage are copied into the following parameters: Employee employee.id, Employee employee.age, Employee employee.wage.

    Please let me know that is how they work together in pictorial format?

    Thanks, Have a great day

    • Nguyen

      Hi Alex & nascardriver,

      "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."

      Could you please explain "the argument is copied into the parameter" in this case?  
      1. Is it the value of the argument that is copied into the parameter?
      2. If yes, What value is it?

      It is very easy to understand and visualize what value(s) is copied into the parameter(s) in the previous chapters.

      Thanks

      • You can think of it like this

  • Merlin

    Hey, dear teacher!

    What do you think about this code? I recognized that it's quite different than your solution and I'm asking,
    if this is legitimate.

    Thank you in anticipation for reading my code and for this great tutorial! :)

    P.S: It outputs:

    Enter an advertising count: 2
    Enter advertising money: 2
    Enter advertising views: 2
    You made 8 for that day!

    • * Line 10, 15, 20, 25, 5, 6, 7: Initialize your variables with brace initializers.
      * Line 29: Limit your lines to 80 characters in length for better readability on small displays.
      * Line 10: Avoid global variables.
      * Line 16, 21, 26: You're accessing an uninitialized variable.
      * Line 10: Avoid abbreviations.

      You're not using @Advertising for anything, give it another try.
      Instantiate an @Advertising in @main, fill it in @main, pass it to @printAndCalculate as an argument.

  • Harper

    I don't understand why I used static_cast but the result is still 0?

  • Dear Teacher,
    Please let me say you that in 1st quiz you state "... what percentage of ads were clicked on by users...". It's okay. But in programme corresponding question is "What percentage of users clicked on the ads?" Given that number of users is unknown, calculation of total earnings is impossible. Then, correct question is what is stated in quiz.
    With regards and friendship
    Georges Theodosiou

  • yadnesh

    Hey can you tell about the different ways of initialization of a struct.
    I went through the following lines of code, and didn't understand what the 6th line meant, but later realized that it was initializing the struct. Correct me if I'm wrong.

    1    /**
    2    * Definition for singly-linked list.
    3    * struct ListNode {
    4    *     int val;
    5    *     ListNode *next;
    6    *     ListNode(int x) : val(x), next(NULL) {}
    7    * };
    8    */

  • Lorenz

    the fact that enums use comma and struct use ";" is just crazy BS... opening space for mistakes... is there any reason why or is it just bad design of C++?

    • Alex

      I presume structs use semicolons because that's how normal non-struct variables are defined. You can use commas in structs if you're defining multiple struct variables in a single line:

      As for why enumerators are comma separated, I'm not sure.

  • Nathan

    I know this is only loosely related, but the chest is at (4,7) and when I try to use it from (3,2), (3,10), (3,4) ect, it returns true.  can you help?

    • * Line 3: Limit your lines to 80 characters in length for better readability on small displays.
      * There's no need for an if-statement, you can return immediately.
      * Enable compiler warnings, read them, fix them.

      You have this

  • Nathan

    I have multiple structs with a lot of members and wont want to constantly pass things like playerPos chest1 chest2 chest3 chest4 ect.  Is there a way to just edit, say, chest1.x without passing it along? like

    Without doing

    because it gets very complicated and tedious very fast

    • > Without doing [...]
      That doesn't work. The @chest1 in @example is not the same as @chest1 in @main, it's a copy. Modifying it in @example doesn't affect it in @main. You need references (Chapter 6 and 7).

      You need to either pass a reference to the chest or to its member. There's no way around that.

      For now, you can return the chest and re-assign it in @main

  • j3fro19

    I don't see the difference between using the data type 'struct' or even create your own data type via 'class'. They both declare objects/variables in the same way. And they both store traits about a variable/object depending on how many data members there are for the given class/struct.

    The only difference between the two I see is for class, you have to specify which data members are public/protected/private.
      
    Is there any simple examples how they are different and when you would use one over the other???

    Slightly different topic:
    The data type 'enum' is similar to the data type 'char' right? Each value is an integer, however with enum, certain characters/string are by default given an integer number, by order of which they are listed.

    • Alex

      I cover the difference between classes and structs in lesson 8.2.

      enum is only similar to char in the sense that both chars and the enumerators are integral types. They're used for different purposes -- chars to hold individual characters, enums to provide enumerators to enumerate sets of options.

  • Darshan

    Hi Alex and nascardriver,

    While passing a struct as a parameter to a function, it gets passed as "pass by value" right? i.e, the original struct does not get modified right?

    You might want mention about that in the turtorial !

    Thank you

  • Arthur

    in line 11 and line 23 I was a little confused by the function names I changed them to figure out how it works but thought I would mention that

    was a little confusing as to which parts where just names and which part makes it work... I had to spoil the answer to learn this section here is what I wrote , just wondering if there is any problem with changing the new line "\n" the way I have it here? or if my formatting is ok now, I started using code::blocks

    think I could use struct to go back to the ball drop program I have that working with a while loop and added rate of descent to the output. thanks for the tutorial I'm getting a lot out of it :)

    • * Line 35: Initialize your variables with brace initializers. You used copy initialization.
      * Line 5, 6, 7, 13: Initialize your variables with brace initializers.
      * Use Code::Block's auto-formatting feature (Line 31).

      Printing the line feed at the end of the line makes sure that the next line is printed properly. If your program crashes after printing, the next output will be on the same line.

  • Paulo Filipe

    @nascardriver, review please? :D

    • Hi Paulo!

      * Line 18: Should be 100.0.
      * Line 29: Initialize your variables with brace initializers.

      You don't need to use << to concatenate string literals for printing. You can just write them one after the other. ex:

      This way, the concatenation happens at compile-time.

  • I am not sure about this way of defining struct variables "a = {1,2,3,4}" (in which a is a struct), due to a little nightmare I had in Go, where this format is recommended. I discovered I forgot a variable inside a struct, and putting it in caused me tons of compiler errors, as now all definitions later on were no longer valid. And by going for "a.a = 1; a.b = 2; a.c = 3; a.d = 4;" I may have to type a lot, but at least I know that nightmare I had in Go is NOT gonna happen :P

    • Hi Jeroen!

      Initialization is faster than assignment, you should go for initialization.
      The problem you described isn't exclusive to structs, it's the same for function arguments.
      C++20 will add designated initializers for structs. Those will allow you to specify which member you want to initialize

      You'll have to update all initializations anyway. Either you get a compile-time error or you experience undefined/undesired behavior at run-time caused by uninitialized or default-initialized variables (The default-initialized ones won't be a problem in most cases).

  • Nathan

    This isn’t about the quiz, but I currently have a problem with a project I keep on a flash drive.  I created the project on a flash drive with visual studio executive and have been unsuccessful in running it on any other computer, regardless of edition. (I am trying to edit it in visual studio community).  When I load the program,  I can see and edit the code (small project, all correct), but the error report consists of 419 errors referring to missing files and such.  Is there a way to fix this?

  • Johannes van der Zwan

    Good day,
    I would like to know if it is posible within a function to find out what the variables are of the structer that is given as a parameter.

  • Barne

    Hi Alex.

    Thanks for the tutorial so far, it’s amazing!

    Something popped up in my head when I was reading the company struct example. What if you need a variable amount of variables?

    Let’s say I make a software to keep track of employee stats, but each company that use it will have a varying amount of employees, (and they want to be able to input everything themselves). Do I just make a struct for employee and then add a crap ton of placeholder members like employee.1, employee.2, etc.? That can’t possibly be the solution. Is there a way to declare variables while running the code?

    Is the solution to this something we will be learning in this tutorial?

    Regards

    • Barne

      To clarify: is there anyway of doing variableName.x where x gets incremented each time a loop is done and thus switching the variable name? Or how would you do the scenario I suggested?

    • Hi Barne!

      You're looking for arrays (Chapter 6).

  • hassan magaji

    Hi Alex,
    is there any note on "Unions" aggregate data type?,
    or are they been deprecated in C++?.

    • Hi Hassan!

      Unions aren't covered by learncpp. They aren't deprecated either.
      But there's not much to them, you should be able to understand them from reading their description on cppreference for example.
      https://en.cppreference.com/w/cpp/language/union

      • hassan magaji

        read it,
        they're crystal clear and concise.
        thanks alot nascar....
        by the way,
        is there a way to implement an array of mixed data type using "unions" and "arrays"?.

        • Yes, but you have to keep track of each element's type, since access via a wrong type produces undefined behavior.

          Also note that this example is wasting 6 bytes (Assuming 8 byte double, 2 byte short).
          From my experience, there's rarely a reason to use unions.

          Tuples are a safer way of mixing types.

  • l1b3rator

    struct Advertising
    {
        int nrOfAds;
        double clickedAds; //no of adds that were clicked
        double avAddIncome; //average income per add
    };

    Advertising getInfo()
    {
        std::cout << "Input the number of ads: ";
        int nrOfAds;
        std::cin >> nrOfAds;

        std::cout << "Input the number of clicked ads: ";
        double clickedAds;
        std::cin >> clickedAds;

        std::cout << " ";

        std::cout << "Input the average income/add: ";
        double avAddIncome;
        std::cin >> avAddIncome;

        Advertising temp = { nrOfAds,clickedAds,avAddIncome };

        return temp;
    }
    void printAdvertising(Advertising temp)
    {
        std::cout << " ";
        std::cout << "You have showed: " << temp.nrOfAds << " adds \n";
        std::cout << " ";
        std::cout << temp.clickedAds << " have been clicked \n";
        std::cout << " ";
        std::cout << "The average income/add was: " << temp.avAddIncome << "\n";
        std::cout << " ";
        std::cout << "Today you have earned: " << temp.avAddIncome*temp.clickedAds*temp.nrOfAds << " dolars";
    }

    int main()
    {
        Advertising temp = getInfo();
        printAdvertising(temp);

        return 0;
    }

    this works but i don't understand why it ignores the empty lines that i intend to insert between. The output does not look very nice. Why does it ignore this std::cout << " "; ?

    • Kyle

      It's not ignoring it. " " is a space, not a newline. If you want an extra line between each sentence, either use

      a. std::cout << std::endl;

      or

      b. "\n";

  • Erk_Forever

    No matter what I do, I can only seem to get a return of zero! What is happening?

    • * Line 5, 6: Initialize your variables with uniform initialization.
      * Line 23, 26, 29: Limit your lines to 80 characters in length for better readability on small displays.

      @numerate is an int, @denominate is an int. int / int = int.  If @denominate > @nomitate, the result is 0. You're casting 0 to a float. You need to cast before dividing.

  • Hi Don!

    Line 1-5 and 6 are unrelated.

    is a struct without a name.

    sets "Payload" as an alias for that anonymous struct.

    Line 6 creates an object of type @Payload.

    I think C couldn't name structs directly, I'm not certain though. Anyway, the syntax taught on learncpp should be preferred.

  • Don

    Do I understand correctly the following struct which I've encountered elsewhere:

    Based on your chapter 4.6 on typedefs, following the typedef keyword the "struct" is the datatype to be aliased. The name of the struct datatype is "Payload" and the bottom line "Payload theData;" indicates that "theData" can be used as an alias for the struct name "Payload". If this is correct, what is the purpose of giving the struct an alias? When is using an alias for a struct name useful?

  • Jon Olive

    Hi - great tutorial - enjoying it immensely.

    [quote] Only use float when you're concerned about memory usage, use double or long double otherwise [/quote]

    Q1 clickPercentage and averageEarnings you have as doubles - but is this precision really necessary? i.e. is a float not totally up to the job in this case? - and is it not good engineering practise to use the least memory-hungry solution regardless of whether this is critical or not?

    Many thanks!

    • Alex

      Double is the default floating point type for C++, so that is what is typically used unless optimizing for space is important. Unless you have millions of elements using a double that could be using a float, the meager memory savings isn't worth optimizing for. There are some good additional answers here.

      • Jon Olive

        Many thanks, Alex.

        I can see that what I somewhat naively assumed to be a fairly simple question turns out to have a lot of underlying stuff to consider. I can see I have something of a mountain to climb - but I have, at least, started the journey to base camp!

Leave a Comment

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