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 nEmployeeID;
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.nEmployeeID = 14; // initialize nEmployeeID 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.nEmployeeId = 14; sJoe.nAge = 32; sJoe.fWage = 24.15; Employee sFrank; // create an Employee struct for Frank sFrank.nEmployeeId = 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.nEmployeeId = 14;
sJoe.nAge = 32;
sJoe.fWage = 24.15;
Employee sFrank; // create an Employee struct for Frank
sFrank.nEmployeeId = 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.
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
5.1 — Control flow introduction
|
Index
|
4.6 — Typedefs
|
5.1 — Control flow introduction
Index
4.6 — Typedefs
Is there a ‘with’ keyword that allows you to edit variables of a structure without having to re-type the name repeatedly [like in Visual Basic]?
Employee sFrank //a variable of the Employee type
with sFrank
.nEmployeeID = 15
.nAge = 28
[end with statement]
// or something like that?
Not that I’m lazy or anything…
PS: I think there are some unwanted borders/shading on this page.
Bye!
Nope, there is no “with” keyword, nor any keyword that mimics that kind of functionality.
As for the unwanted borders/shading, what OS and browser are you running?
Internet Explorer on Vista.
The problem’s only on THIS page, so I was assuming it was a HTML error. Not that it really matters if I’m the only one who has to suffer from it. =D
Thanks!
I’m seeing the borders and shading, too. I think you might not have terminated the HTML <code> tag on this page, shortly after your last example, at the reference to “sMyCompany.sCEO.fWage”. Looks like you used <code> instead of </code>.
By the way, I’ve been following the tutorials here for a week or so now. They’re very well written and really helpful. Thanks!
Wow, nice catch! I fixed the closing tag. Let me know if you are still having problems.
/* Program of Structure Exerice 1 */
#include <iostream.h>
#include <conio.h>
struct web
{
int adv;
float rate;
float click;
};
float money(int adv,float rate,float click)
{
float totalearning;
totalearning = (adv * rate * click);
return totalearning;
}
web mny;
void main()
{
float res;
clrscr();
cout << "n How many advertise was shown: ";
cin >> mny.adv;
cout << "n What was rate of click : ";
cin >> mny.rate;
cout << "n Average of earning per click : ";
cin >> mny.click;
res = money(mny.adv , mny.rate , mny.click);
cout << "n The Total earning is " << res;
getch();
}
/* End of Program */
We can pass a structure to a function.
But can a function return a structure of data?
Cheers.