Search

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

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

249 comments to 4.7 — Structs

  • AMG

    Alex,
    I think struct name Rectangle would be more intuitive than Triangle. Regards.

  • #include<iostream>
    #include<string>
    using namespace std;
    struct employee
    {
        int age;
        int salary;
    };
    struct branch
    {
    const string name;
    };
    struct company
    {
    const string company_name;
    };
    struct company_headquaters
    {
        employee name;
        branch br;company comp_name;
    const string comp_head;
    };
    int main()
    {
         company_headquaters New={{32,32000},{"noida. sec-62"},{"microsoft"},"san francisco, California"};
         cout<<New.br;
    }
    so I was trying to output strings, as you can see above but it didn’t work.
    any explaination why?

  • hey Alex, it seems like i don’t understand the exact meaning and task of return keyword.
    can you help me, please?

  • Anon

    Hello thank for these tutorials.  I don’t know if you are still answering questions, but I am having problems printing the decimal result.  I am using static cast, but It is still print out the number as if it was an integer.  

    This is my code

    This is my output:

    [anon@Laptop Documents]$ g++ test.cpp
    [anon@Laptop Documents]$ ./a.out
    Pick your number: 2012
    Pick your number: 103
    Pick your number: 1
    Pick your number: 10
    2012\103 = 19
    1\10 = 0
    2012\1030 = 1
    [anon@Laptop Documents]$

    • Alex

      The issue is that you’re doing an integer division (int / int) and casting the result to a double, rather than doing a floating point division. You should convert one of the integer operands to a double and then do the division.

  • Flo

    I kinda like to keep my main() as clean as possible, so I put everything inside functions:

    Also, yes, this tutorial is the best of the best of the best. It keeps a perfect pace, that doesn't make me feel bored at moments, while maintaining a very indepth explaination on every topic.
    Alex, thank you very much!
    Btw, do you know other languages, like java, or perhaps python?

  • zeivhann

    I thought I’d share mine! I spent entirely too much time on this doing fun formatting stuff. But in case it helps anyone.

  • Eric

    I cannot understand why this won’t compile. I figured it would be easy to use a function to call for the input variables.

    • Alex

      Visual Studio 2017 complains that you’re using uninitialized local variable ads. This happens because you’re passing ads to obtainInfo before initializing ads.

      Two changes you should make:

      1) Because obtainInfo() is getting data from the user, not from main(), ads should be declared as a local variable, not a function parameter.
      2) obtainInfo() passes ads back to main(), but main discards the result. This should be assigned to ads instead.

      • Eric

        Hello again Alex,

        I appreciate your speedy response and the help it gave me. By placing the code that originally was in the obtainInfo() function into main() directly it works perfectly. However, I am still confused on how I could create an outside function to do this. Unless this idea is just not good coding practice (in which case I don’t need to know how to do it lol)

        • Alex

          1) Your obtainInfo() function was good, just ads should have been a local variable to the function, not a function parameter.
          2) Instead of obtainInfo(abs), you should have done abs = obtainInfo().

      • erad

        Hi Alex,

        In your first response to his OP, you wrote:
        "1) Because obtainInfo() is getting data from the user, not from main(), ads should be declared as a local variable, not a function parameter."

        Are you trying to say that, from a performance/style standpoint, it is better to have a local variable OR that the program wouldn’t compile so long as he used a function parameter?

        Because, in my view, if he just did what you stated in your second point (in line 32 of his OP), the code would have compiled and worked! Right?

        • Alex

          Either would compile, but a local variable is a better choice. Function parameters should only be used when the caller is expected to provide the values for that variable. In this case, the user is expected to provide the data, so a local variable should be used. Otherwise the caller is having to create and pass in a variable whose value will just be overwritten -- where’s the sense in that?

          • erad

            How about cases (which I’ve seen before) where the function’s formal parameter is a reference parameter which affords the function the ability to effect changes in the caller? Usually, in those cases, the function is used to accept input data from the user.

            Moreover, sometimes one wants to pass multiple data (not necessarily an aggregate data-type like struct) back to the caller, how would one do this by restricting oneself to only local variables?

            Am I missing something here?

            • Alex

              Yes, you can use reference parameters as “out” parameters, where the function alters the value of the caller’s argument so the caller can use the updated value. But generally you should avoid this paradigm unless there’s no better options. One time when there might be no better options is when you want the function to pass back multiple values.

              You’re not missing anything, you’re just getting ahead of yourself. This stuff is all discussed in chapter 7. 🙂

  • Derek D

    Hello!
    On Quiz 1, I couldn’t figure out why my code wouldn’t work, so I checked the solution, and even it wouldn’t work! I get the compiler error that ad.adsShown, ad.clickThroughRatePercentage, and ad.averageEarningsPerClick were undeclared identifiers. Do you know why this is happening and how I can fix it?

    Also, I’m loving this tutorial/class you’ve made!

    • Alex

      I can’t think of a good reason why this might not be working for you. Are there any other errors above this one?

      • Derek D

        I figured out the issue. In my original code and your code, I had #include <iostream> before #include "stdafx.h". This also caused some problems in my other projects as well…

        Do you know why the order matters?

        • Alex

          Visual studio requires #include “stdafx.h” to be first if you’re using precompiled headers. It assumes everything before that line has already been precompiled.

  • Jim Smith

    At quiz 1, instead of "what percentage of users clicked on ads" don’t you mean "what percentage of ads were clicked on by users"?

  • Jack

    Hello,

    Thank you for these lessons!

    Could you help me better understand why the following code will not calculate the correct answer? Wouldn’t the parenthesis take first precedence so the division should be executed first and then multiplied together?

    --

    #include <iostream>

    using namespace std;

    struct Fraction
    {
        int num;
        int denum;
    };

    void mult_fract(Fraction f1, Fraction f2){
        cout << "Multiplication of fractions: " <<
        static_cast<float>(f1.num / f1.denum) * (f2.num / f2.denum);
    }

    int main()
    {
        cout << "Part B" << endl;
        Fraction f1 = {1, 2};
        Fraction f2 = {1, 4};
        mult_fract(f1, f2);
        return 0;
    }

    • Alex

      Yes. First, f1.num / f1.denum will happen. Because these are integers, this will be an integer division. Any decimal value will be discarded before the cast to float.

      You need to cast one of the values to a float _before_ doing the division, not after.

  • Pravin Gaikwad

    Can we declared static member inside structure ? how to access static member of structure?

    • Alex

      Yes, you can. I discuss this in lesson 8.11 (using classes, but it works the same for structs).

      • Pravin Gaikwad

        Thanks.
        Actually i have not assigned value to static variable of struct and thats why compiler gives error variable is not declared when accessing it.

  • I was just reading up a bit on the history and future of C++ and I was wondering if this website will include the new features of the coming (if it hasn’t already) C++ 17 (and possibly C++ 20 when and if it comes in that year).

    • Alex

      My intent is to cover the major parts of C++17 once it is released. How timely I’m able to do that is in question, as my time for writing is somewhat limited these days.

  • samir sabbah

    #include <iostream>
    #include <typeinfo>
    #include <string>

    using namespace std;

    struct Employee
    {
        short id;
        int age;
        double wage;
    };

    void printInformation(Employee employee)
    {
        cout<<" ID : "<<employee.id<<"\n";
        cout<<" AGE : "<<employee.age<<"\n";
        cout<<" WAGE : "<<employee.wage<<endl;;\
    }

    int main()
    {
        Employee joe = {1,30,3000.0};
        Employee frank = {2,25,2500.0};

        cout<<" Please enter the employee’s name : ";
        std::string name;
        cin>>name;

        if (name == "frank")
            cout<<"frank’s info : "<<"\n";
            printInformation(joe);
        
        if (name == "joe")
            cout<<"joe’s info : "<<"\n";
            printInformation(frank);

        return 0;
    }

    Hello Alex,
    i made a similar code to yours but i added if statements to check which name’s should be printed out…
    but each time i run the code it shows me both info as if i didn’t do anything… can you clarify the problem please…

    THANKS FOR THE HELP.

    • Alex

      Without using curly braces, only the statement immediately proceeding the if statement is considered part of the if. Since you want two lines to execute as part of the if condition, you need to wrap them in a block. e.g.

      [code]
      if (name == “frank”)
      { // add this block
      cout<<"frank’s info : "<<"\n"; printInformation(joe); // this needs to be inside the block so it's considered part of the if }

  • One Manual

    I have a problem with this concept, please respond :(.

    i made this:

    #include "stdafx.h"
    #include <iostream>

    struct daytotal

    {
        double readers;
        double percentage;
        
    };

    daytotal firstday = { 5626, 15 };

    double readersAndAds(daytotal readers)
    {

        return firstday.readers;

    }

    int main()
    {
        
            
        
        readersAndAds(firstday.readers);
        
        std::cout << readersAndAds << "n";

        return 0;
    }

    Im trying to call the function readersAndAds with firstday.reader value (5626), but i get a red error underlined in "readersAndAds(firstday.readers);", the error says "no suitable constructor exist to convert from "double" to "daytotal". I dont understand what is happening :(. Tried to change int main to double but i couldnt either. This is a problem from a bigger program, dunno what is happening.

    • Alex

      The issue here is that your readersAndAds() function is taking a daytotal struct as a parameter, but you’re passing in an argument firstday.readers, which is a double. The compiler is telling you that it doesn’t know how to convert a double to a daytotal.

  • I get the error "C:\Users\GigaTech\Documents\Programming\C++\Conversion\structs\main.cpp|7|error: invalid use of non-static data member ‘Advertising::adsShown’" on line 7. How do I fix this?

    • Alex

      Your advertising struct is just a type definition -- it doesn’t actually allocate any memory to hold values. In order to do that, you need to instantiate an object of the Advertising type:

      Instead of accessing elements via Advertising::adsShown, you’ll access them through the instantiated object ad.adsShown.

  • Gábor

    What the non-static member means? And what would be the static member?

    • Alex

      A non-static member is a member of a struct (or class) that is defined without using the static keyword. In other words, almost everything.

      We talk about static members in chapter 8. They’re not used very often, so don’t worry about them for now.

  • Jason

    I’m really enjoying these tutorials.  Thanks for doing it.  

    Here is my code for the second quiz.  Any feedback is appreciated.

  • kleinstein

    "Assigning to structs

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

    -> Prior to C++11, if "if" … 🙂

  • Mihai

    Hi, Alex
    is it okay if i used

    for multiplying?
    also thanks for your guides, they help a lot :3

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter