Navigation



4.7 — Structs

There are many instances in programming where we need more than one variable in order to represent something. 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:

char strName[20];
int nBirthYear;
int nBirthMonth;
int nBirthDay;
int nHeight; // in inches
int nWeight; // in pounds

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 more people, 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 type is the struct. A struct (short for structure) allows us to group variables of mixed data types together into a single unit.

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:

struct Employee
{
    int nID;
    int nAge;
    float fWage;
};

This tells the compiler that we are defining a struct named Employee. The Employee struct contains 3 variables inside of it: two ints and a float. These variables are called members (or fields). Keep in mind that the above is just a declaration — even though we are telling the compiler that the struct will have variables, no memory is allocated at this time.

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

Employee sJoe;

sJoe is a variable of type Employee. As with normal variables, declaring a variable allocates memory for that variable. Typically, the size of a struct is the sum of the size of all it’s members. In this case, since each integer is 4 bytes and a float is 4 bytes, Employee would be 12 bytes. However, some platforms have specific rules about how variables must be laid out in memory — consequently, the compiler may leave gaps between the variables. As a result, we can say the struct will be at minimum 12 bytes.

To find out the exact size of Employee, we can use the sizeof operator:

cout << "The size of Employee is " << sizeof(Employee);

On the author’s Pentium 4, this prints The size of Employee is 12.

When we declare a variable such as Employee sJoe, sJoe 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). As with normal variables, struct member variables are not initialized, and will typically contain junk. We must initialize them manually. Here is an example of using the member selection operator to initialize each member variable:

Employee sJoe;
sJoe.nID= 14; // initialize nID within sJoe
sJoe.nAge = 32; // initialize nAge within sJoe
sJoe.fWage = 24.15; // initialize fWage within sJoe

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

Employee sJoe; // create an Employee struct for Joe
sJoe.nID = 14;
sJoe.nAge = 32;
sJoe.fWage = 24.15;

Employee sFrank; // create an Employee struct for Frank
sFrank.nID = 15;
sFrank.nAge = 28;
sFrank.fWage = 18.27;

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 the members all have the same name, this provides consistency across multiple variables of the same type.

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

int nTotalAge = sJoe.nAge + sFrank.nAge;

if (sJoe.fWage > sFrank.fWage)
    cout << "Joe makes more than Frank" << endl;

// Frank got a promotion
sFrank.fWage += 2.50;

// Today is Joe's birthday
sJoe.nAge++;

Another 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:

#include <iostream>

void PrintInformation(Employee sEmployee)
{
    using namespace std;
    cout << "ID:   " << sEmployee.nID << endl;
    cout << "Age:  " << sEmployee.nAge << endl;
    cout << "Wage: " << sEmployee.fWage << endl << endl;
}

int main()
{
    Employee sJoe; // create an Employee struct for Joe
    sJoe.nID = 14;
    sJoe.nAge = 32;
    sJoe.fWage = 24.15;

    Employee sFrank; // create an Employee struct for Frank
    sFrank.nID = 15;
    sFrank.nAge = 28;
    sFrank.fWage = 18.27;

    // Print Joe's information
    PrintInformation(sJoe);

    // Print Frank's information
    PrintInformation(sFrank);

    return 0;
}

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!

PrintInformation() uses the Employee struct passed to it to print out employee information to the screen. The above program outputs:

ID:   14
Age:  32
Wage: 24.15

ID:   15
Age:  28
Wage: 18.27

Structs can contain other structs. For example:

struct Company
{
    Employee sCEO; // Employee is a struct within the Company struct
    int nNumberOfEmployees;
};

Company sMyCompany;

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

This selects the sCEO member from sMyCompany, and then selects the fWage member from within sCEO.

Initializer lists

Initializing structs 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.

struct Employee
{
    int nID;
    int nAge;
    float fWage;
};

Employee sJoe = {1, 42, 60000.0f}; // nID=1, nAge=42, fWage=60000.0

You can use nested initializer lists for nested structs:

struct Employee
{
    int nID;
    int nAge;
    float fWage;
};

struct Company
{
    Employee sCEO; // Employee is a struct within the Company struct
    int nNumberOfEmployees;
};

Company sCo1 = {{1, 42, 60000.0f}, 5};

A few final notes on structs

The “m_” Hungarian Notation prefix for members is typically not used for structs, even though structs contain members. This is (in part) because all variables in a struct are members! Consequently, labeling them with a “m_” prefix is somewhat redundant.

It is common to declare structs in a header file, so they can be accessed by multiple source files.

The class aggregate data type builds on top of the functionality offered by structs. Classes are at the heart of C++ object-oriented programming. Understanding structs is the first step towards object-oriented programming!

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 users clicked on ads (as a floating point number between 0 and 1), 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) Write a struct to hold a fraction. The struct should have a integer numerator and a 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.

Quiz Answers

1) Show Solution

2) Show Solution

5.1 — Control flow introduction
Index
4.6 — Typedefs

88 comments to 4.7 — Structs

  • Knight

    I am using Code::Blocks IDE. I have this code

    1.cpp
    —————-
    #include
    using namespace std;
    void printEmployeeInfo(Employee ra) {
    cout << "age is : " << ra.age << endl;
    cout << "height is : " << ra.height << endl;
    cout << "sal is : " << ra.sal << endl;
    }

    2.cpp
    —————

    #include
    #include
    #ifndef STRUCT_H
    #define STRUCT_H

    #include “struct.h”

    #endif

    using namespace std;
    int main() {
    Employee r = {1,2,3.0f};
    printEmployeeInfo(r);
    return 0;
    }

    struct.h
    ————————
    struct Employee {
    int age;
    int height;
    float sal;
    };

    I am getting following compilation/linking error when i execute it

    mingw32-g++.exe -Wall -g -c C:\Users\rawat\Documents\CPP\cpp\2.cpp -o obj\Debug\2.o
    C:\Users\rawat\Documents\CPP\cpp\2.cpp: In function ‘int main()’:
    C:\Users\rawat\Documents\CPP\cpp\2.cpp:71: error: parameter 1 of ‘void printEmployeeInfo(r)’ has incomplete type ‘r’
    C:\Users\rawat\Documents\CPP\cpp\2.cpp:61: warning: unused variable ‘g_nX’
    Process terminated with status 1 (0 minutes, 4 seconds)
    1 errors, 1 warnings

    But, If i move printEmployeeInfo function from 1.cpp to 2.cpp, it works fine. Can some help me with this.

  • Knight

    Never mind, i fount the issue.

  • DrSuse

    I had trouble with :

    void Multiply(Fraction sF1, Fraction sF2)

    because in section 2.1 Basic Addressing and Variable Declaration, it states:

    “The first mistake is declaring each variable as int (or whatever type it is) in sequence. This is not a bad mistake because the compiler will complain and ask you to fix it.

    int nValue1, int nValue2; // wrong (compiler error)

    int nValue1, nValue2; // correct”

    where instead I typed (something like) :
    void Multiply(Fraction sF1, sF2)
    and got compiler errors all over the pace.

    Am I to assume it’s different for function parameters?

    • codeez

      “Am I to assume it’s different for function parameters?”.

      Up to this point in the lessons, I’ve also noticed this hasn’t been explained, but you are right about it for sure!

  • KanedaSyndrome

    My results:

    main.cpp

    #include <iostream>
    #include "functions2.h"
    using namespace std;

    advertising campaign1;
    fraction frac1, frac2;

    int main()
    {
    campaign1 = getData(campaign1);
    printResults(campaign1);

    cout << endl << "NEXT PART OF THE EXERCISE!" << endl;

    cout << "First fraction" << endl;
    frac1 = getFrac(frac1);
    cout << "Second fraction" << endl;
    frac2 = getFrac(frac2);
    fracProduct(frac1, frac2);

    cin.get(); //for halting the program
    cin.ignore(); //for halting the program
    return 0;
    }

    functions2.cpp

    #include <iostream>
    #include "functions2.h"
    using namespace std;

    advertising getData(advertising x)
    {
    cout << "Input the number of adds in the campaign: ";
    cin >> x.numberAdds;
    cout << "Input the hit rate of the campaign: ";
    cin >> x.hitRatio;
    cout << "Input the average revenue per hit: ";
    cin >> x.avgRevenuePerHit;
    return x;
    }

    void printResults(advertising x)
    {
    float result = x.numberAdds*x.hitRatio*x.avgRevenuePerHit;
    cout << endl << "The campaign has resulted in a total revenue of: " << result << endl;
    }

    fraction getFrac(fraction x)
    {
    cout << "Input a numerator: ";
    cin >> x.numerator;
    cout << "Input a denominator: ";
    cin >> x.denominator;

    return x;
    }

    void fracProduct(fraction x, fraction y)
    {
    float result = static_cast<float>(x.numerator*y.numerator)/(x.denominator*y.denominator);

    cout << "Multiplying the two fractions yields: " << result << endl;
    }

    functions2.h

    #ifndef FUNCTIONS2_H
    #define FUNCTIONS2_H

    struct advertising
    {
    int numberAdds;
    float hitRatio;
    float avgRevenuePerHit;
    };

    struct fraction
    {
    int numerator;
    int denominator;
    };

    advertising getData(advertising x);
    void printResults(advertising x);
    fraction getFrac(fraction x);
    void fracProduct(fraction x, fraction y);

    #endif

    ~KanedaSyndrome

  • ZazaParkadze

    #include

    using namespace std;
    const float PERCENTT = 0.2;

    struct Adcompany
    {
    int nNumberOfAds;
    double nNumberOfVizitors;
    };

    // calculates and prints the income per day

    void adrevenue(Adcompany anyDay)
    {
    static int nday=1;
    float sum;
    const float fPricePerClick = 0.05;
    const float fPricePerAdSeen = 0.0;
    float nNumberOfClicks= PERCENTT * anyDay.nNumberOfVizitors;
    sum = fPricePerClick * nNumberOfClicks + fPricePerAdSeen * anyDay.nNumberOfAds * anyDay.nNumberOfVizitors;
    cout << "I've made easy " << sum <<" dollars "<< nday <<"th day" << endl;
    nday++;

    }

    int main()
    {
    Adcompany firstDay;
    cout << "enter number of Ads: " <> firstDay.nNumberOfAds ;
    cout << "enter number of vizitors: " <> firstDay.nNumberOfVizitors ;
    adrevenue (firstDay); // calculates and prints the income of first day

    // we may print out the earnings of second day and so on ..

    Adcompany secondDay = {2,1000.0};
    Adcompany thirdDay = {3,2000.0};
    Adcompany fourthDay = {4,3000.0};
    adrevenue(secondDay);
    adrevenue(thirdDay);
    adrevenue(fourthDay);
    return 0;
    }

  • Blackout
    #include<iostream>
    
    int main()
    {
        using namespace std;
        cout<<"Enter how many ads were generated:"<<endl;
        int x;
        cin>>x;
        cout<<"Enter the percentage of the people who clicked on the ads(in decimal form):"<<endl;
        float  y;
        cin>>y;
        cout<<"Enter the average amount of money generated by each click:"<<endl;
        float z;
        cin>>z;
    
        int NumberOfAds= x;
        float PercentageOfClicks=y;
        float AverageAmountOfMoney=z;
    
        cout<< "The Amount of Money You Made Is: "<<"$" <<NumberOfAds*PercentageOfClicks*AverageAmountOfMoney<<endl;
        return 0;
    }
    

    This is what I did, I didn’t use structs because I wasn’t sure how, but it works.

  • Speeds03

    This is how I did it. It worked perfectly. But correct me if I should have done it another way. By the way, I pre-set the values for the variables because we aren’t really getting any user data to actually calculate a result.

    #include “stdafx.h”
    #include

    using namespace std;

    struct MoneyPerDay
    {
    int nAdShown; // 45 Ads shown each month
    long float fAdsClicked; // Amount of Ads clicked by users
    long float fAmountPerClick; // Each Ad click revenue is $.10
    long float nUsersPerDay;

    };

    void PrintMoneyEarnedPerDay(MoneyPerDay sMoneyPerDay)
    {
    cout << "Number of users per day: " << sMoneyPerDay.nUsersPerDay << endl;
    cout << "Number of Ads shown: " << sMoneyPerDay.nAdShown << endl;
    cout << "Number of Ads clicked: " << sMoneyPerDay.fAdsClicked << endl;
    cout << "Amount of money per Ad click: " << sMoneyPerDay.fAmountPerClick << endl;
    }

    int main()
    {
    MoneyPerDay sMonday;
    sMonday.nUsersPerDay = 10780;
    sMonday.nAdShown = 45;
    sMonday.fAdsClicked = 20;
    sMonday.fAmountPerClick = .10;

    MoneyPerDay sTuesday;
    sTuesday.nUsersPerDay = 20678;
    sTuesday.nAdShown = 45;
    sTuesday.fAdsClicked = 20;
    sTuesday.fAmountPerClick = .10;

    // Prints stats for monday
    cout << "Monday stats— " << endl;
    PrintMoneyEarnedPerDay(sMonday);
    cout << "The result is: $" << sMonday.nUsersPerDay / sMonday.fAdsClicked * sMonday.fAmountPerClick << endl;

    cout << " " << endl;

    // Prints stats for tuesday
    cout << "Tuesday stats— " << endl;
    PrintMoneyEarnedPerDay(sTuesday);
    cout << "The result is: $" << sTuesday.nUsersPerDay / sTuesday.fAdsClicked * sTuesday.fAmountPerClick << endl;

    cout << " " << endl;

    return 0;
    }

  • codeez

    Long float is kind of an oxymoron I think? VS2012 gives warnings:

    nonstandard extension used : long float c:user

    If you meant double the size of float then use ‘double’, but seeing as your data doesn’t exceed a float though, just the float would’ve done. :)

    You didn’t need the blanks here: cout << " " << endl; // just do: cout << endl;

    Keep coding. :)

  • hmm. I can’t get it to print out the detritus.


    cout << "Joe takes up " << sideof(Joe) << " bytes." << endl;
    cout << Joe.age << endl;

    only prints out “Joe takes up 12 bytes.”

    This is on EC2 Ubuntu 12.04 default g++ install from repository.

  • stevenhall

    Can anybody please help? I can’t for the life of me work out why my code doesn’t work properly. I can’t see any significant difference (apart from names chosen) between this and the model code given.

    #include <iostream>

    struct AdRevenue {
    int nAds; // number of ads shown to users
    float fPercentClicked; // percent of ads that were clicked
    float fAverageEarned; // average earned per ad
    };

    void printAdRevenue(AdRevenue sAdRev)
    { using namespace std;
    cout << "Number of ads: " << sAdRev.nAds << endl;
    cout << "Percentage of ads clicked:\t\t" << sAdRev.fPercentClicked << endl;
    cout << "Average earned per ad:\t\t" << sAdRev.fAverageEarned << endl;
    cout << "Revenue is:\t\t" << (sAdRev.nAds*sAdRev.fPercentClicked*sAdRev.fAverageEarned);
    }

    int main()
    { using namespace std;
    cout << "Please enter the number of ads shown to users: ";
    AdRevenue sUser_struct;
    cin >> sUser_struct.nAds;
    cout << "Please enter the percentage of ads clicked (between 0 and 1): ";
    cin >> sUser_struct.fPercentClicked;
    cout << "Please enter the average earned per ad: ";
    cin >> sUser_struct.fAverageEarned;
    printAdRevenue(sUser_struct);
    return 0;
    }

    It builds and runs as far as the call to printAdRevenue then just prints ‘(lldb)’ in blue. I’m using XCode.

  • Zidane

    //This is how I did it. Please comment ;)

    #include
    using namespace std;

    struct Adverts
    {
    unsigned short nAdsShown;
    float fClickRate;
    float fAveEarnPerClick;
    };

    Adverts GetAdsData()
    {
    Adverts Ads;
    cout<>Ads.nAdsShown;
    cout<>Ads.fClickRate;
    cout<>Ads.fAveEarnPerClick;
    return Ads;
    }

    void PrintAdsResult(Adverts Ads)
    {
    cout<<"Total of adverts shown:\t\t"<<Ads.nAdsShown<<endl;
    cout<<"Click through rate:\t\t"<<Ads.fClickRate<<endl;
    cout<<"Average earnings per click:\t"<<Ads.fAveEarnPerClick<<endl;
    cout<<"Total earnings for the day:\t$"<<Ads.nAdsShown * Ads.fClickRate * Ads.fAveEarnPerClick<<endl;
    }

    int main()
    {
    PrintAdsResult(GetAdsData());
    return 0;

    }

  • Zidane

    Ofcourse, what you see may sometimes not make sense for WordPress omits many fractions of words such as for example cin, iostrea(m), etc. But you get the idea.

  • Zidane

    #include
    using namespace std;

    struct Fraction
    {
    unsigned short nNumerator;
    unsigned short nDenominator;
    };

    Fraction GetFractionData()
    {
    Fraction Fra;
    static int c=1; // ‘c’ is a Fraction Identifier
    cout<<"Fraction "<<c++<<endl;
    cout<>Fra.nNumerator;
    cout<>Fra.nDenominator;

    return Fra;
    }

    void PrintMultiplyFractions(Fraction x, Fraction y)
    {
    cout<<x.nNumerator<<"/"<<x.nDenominator<<
    " * "<<
    y.nNumerator<<"/"<<y.nDenominator<<
    " = "<<
    (static_cast(x.nNumerator * y.nNumerator)/(x.nDenominator * y.nDenominator))<<
    endl;
    }

    int main()
    {
    PrintMultiplyFractions(GetFractionData(),GetFractionData());
    return 0;

    }

  • thunder boldt

    so this dose what exsakly

  • thunder boldt

    pordon spelling

  • Arthur_

    Why do you (for the second question) declare the numerator and denominators as integers when you know you are going to need them as floats later? Wouldn’t it be more logical to declare them as floats or longs and then you don’t have to static_cast them later?

  • Leolas

    Hi, this is my code for the question 2; i made it slightly different, so i would like to your opinions/corrections :) . However, i forget the cast for the float, so i think that my code is doing integer division :s, well, i would very appreciate your advices or something :)

    #include
    using namespace std;

    struct Fraction
    {
    int nNumerator1;
    int nDenominator1;
    int nNumerator2;
    int nDenominator2;
    };

    void Multiply(Fraction Elements)
    {
    cout << "The product of the two fractions is: " << Elements.nNumerator1 / Elements.nDenominator1 * Elements.nNumerator2 / Elements.nDenominator2;
    }

    int main()
    {
    Fraction Elements;
    cout <> Elements.nNumerator1;
    cout <> Elements.nDenominator1;
    cout <> Elements.nNumerator2;
    cout <> Elements.nDenominator2;

    Multiply(Elements);
    return 0;
    }

  • LLoydR

    Hello.

    I have done the second part of the test and it works fine. I just have a question. I will add my code first:


    STRUCTFUNCTIONS.H

    #ifndef STRUCTFUNCTIONS_H
    #define STRUCTFUNCTIONS_H

    struct Fractions
    {
    int nNumerator;
    int nDenominator;
    };

    #endif


    STRUCTFUNCTIONS.CPP

    #include "stdafx.h"
    #include "stuctFunctions.h"
    #include
    using namespace std;

    void MultiplyFractions (Fractions sFraction1, Fractions sFraction2)
    {
    cout << "Your total value is: " <<
    static_cast (sFraction1.nNumerator * sFraction1.nDenominator)
    / (sFraction2.nNumerator * sFraction2.nNumerator) << endl;

    }


    STRUCTTESTP4.CPP

    #include "stdafx.h"
    #include "stuctFunctions.h"
    #include
    using namespace std;

    void MultiplyFractions (Fractions sFraction1, Fractions sFraction2);

    int main()
    {
    Fractions sFraction1;
    cout << "Insert the first numerator Number: " <> sFraction1.nNumerator;
    cout << "Insert the first Denominator Number: " <> sFraction1.nDenominator;

    Fractions sFraction2;
    cout << "Insert the second numerator Number: " <> sFraction2.nNumerator;
    cout << "Insert the second Denominator Number: " <> sFraction2.nDenominator;

    MultiplyFractions(sFraction1, sFraction2);

    return 0;
    }

    my question is: is there anyway that I can call my struct in a different .cpp file instead of having it in a .h file?

    I know that it would be a lot better to have it in a .h file, but I am just being curious, lol.

You must be logged in to post a comment.