Structures
The structure feature is a carry over from "C". Since "C" provides most of what "C" has it provides support for structures.
A structure can be used to group variables . As an example of declaring structures and using them.
Ex:
#include <iostream>using namespace std ;struct Circle { double radius; double diameter; double area; };int main(){ Circle object1 ; object1.radius = 2.4 ; object1.diameter = 2 * object1.radius ; cout << "object1.radius: " << object1.radius << " object1.diameter: " << object1.diameter << endl ; return(0) ;}
Output:
[amittal@hills Structures]$ ./a.out
object1.radius: 2.4 object1.diameter: 4.8
A struct object is created with the variable "object1" and then using the dot notation the variables are set. A struct's data members are public.
A structure can have a constructor similar to a class.
Ex:
#include <iostream>using namespace std ;struct Circle { double radius; double diameter; double area; Circle() { cout << "Constructor" << endl ; } };int main(){ Circle object1 ; object1.radius = 2.4 ; object1.diameter = 2 * object1.radius ; cout << "object1.radius: " << object1.radius << " object1.diameter: " << object1.diameter << endl ; return(0) ;}
The above example shows the constructor getting called and an output statement printing the structure object's parameters. We can use the Structure as a Class object . The only difference is that the a structure's data members are public by default and a class's data members are private by default.
Ex:
#include <iostream>using namespace std ;struct Circle { double radius; double diameter; double area; };int main(){ Circle object1 ; object1.radius = 2.4 ; object1.diameter = 2 * object1.radius ; Circle object2 ; object2.radius = 2.4 ; object2.diameter = 2 * object1.radius ; if( object1 == object2 ) cout << "Two objects are equal." << endl ; else cout << "Two objects are not equal." << endl ; return(0) ;}
If we try to compile the above we get a compiler error. We cannot compare 2 objects of Circle . We can compare the data members.
#include <iostream>using namespace std ;struct Circle { double radius; double diameter; double area; };int main(){ Circle object1 = {2, 4, 25.12 }; cout << "object1.radius: " << object1.radius << " object1.diameter: " << object1.diameter << endl ; return(0) ;}
Output:
[amittal@hills Structures]$ ./a.out
object1.radius: 2 object1.diameter: 4
The above example shows how we can initialize a Circle object with some numerical values.
It is possible to declare a structure without a name. We can specify the object variable right after the structure definition.
#include <iostream>using namespace std ;//A single variable//Can't create more variablesstruct { double radius; double diameter; double area; } object1 ;int main(){ // Circle object1 ; object1.radius = 2.4 ; object1.diameter = 2 * object1.radius ; cout << "object1.radius: " << object1.radius << " object1.diameter: " << object1.diameter << endl ; return(0) ;}
Note that it is also possible to define classes this way. Such classes are called anonymous classes.
File: "c1.cpp"
#include <iostream>using namespace std ;//A single variable//Can't create more variablesclass { public: double radius; double diameter; double area; } object1 ;int main(){ // Circle object1 ; object1.radius = 2.4 ; object1.diameter = 2 * object1.radius ; cout << "object1.radius: " << object1.radius << " object1.diameter: " << object1.diameter << endl ; return(0) ;}
Partial Initialization
We do not have to initialize a structure object with all the values.
Ex:
#include <iostream>using namespace std ;struct Circle { double radius; double diameter; double area; };int main(){ Circle object1 = {2 }; cout << "object1.radius: " << object1.radius << " object1.diameter: " << object1.diameter << endl ; return(0) ;}
Output:
[amittal@hills Structures]$ ./a.out
object1.radius: 2 object1.diameter: 0
In the above case the other data variables take on their default values which in this case is 0 .
Arrays of Structures
Ex:
#include <iostream>using namespace std ;/* Arrays of objects*/struct Circle { double radius; double diameter; double area; };int main(){ Circle circleArray[2] ; circleArray[0].radius = 2 ; circleArray[0].diameter = 4 ; circleArray[1].radius = 3 ; circleArray[1].diameter = 6 ; cout << "circleArray[0].radius: " << circleArray[0].radius << " circleArray[0].diameter: " << circleArray[0].diameter << endl ; cout << "circleArray[1].radius: " << circleArray[1].radius << " circleArray[1].diameter: " << circleArray[1].diameter << endl ; return(0) ;}
Output:
[amittal@hills Structures]$ ./a.out
circleArray[0].radius: 2 circleArray[0].diameter: 4
We can have arrays of structures as shown in the above example.
Ex:
#include <iostream>using namespace std ;/* Arrays of objects*/struct Circle { double radius; double diameter; double area; };int main(){ //Initialization of array structures Circle circleArray1[2] = { { 2 , 4, 25.12 } , { 1, 2 , 3,14 } } ; return(0) ;}
Nested Structures:
Ex:
#include <iostream>using namespace std ;/*Nested Structure*/struct Circle { double radius; double diameter; double area; };struct Shape { Circle circleObject ; int x1 ;};int main(){ Shape shape1 ; shape1.circleObject.radius = 1 ; shape1.circleObject.diameter = 2 ; shape1.circleObject.area = 3.14 ; return(0) ;}
We can have nested structures also as shown in the above example.
Using structures as arguments and returning structure objects from functions.
Ex:
#include <iostream>using namespace std ;/*Passing Structure objects as arguments*/struct Circle { double radius ; double diameter ; double area ; }; Circle fillCircle( Circle object1 ){ Circle object2 ; object2.radius = object1.radius ; object2.diameter = object1.diameter ; object2.area = object1.area ; return object2 ;} Circle& fillCircle1( Circle object1 ){ Circle object2 ; object2.radius = object1.radius ; object2.diameter = object1.diameter ; object2.area = object1.area ; return object2 ;}int main(){ Circle object1 ; Circle object2 = fillCircle1( object1 ) ; Circle object3 = fillCircle( object1 ) ; return(0) ;}
The above example shows how structure objects can be passed to a function and returned from a function. The "fillCircle1" function has a mistake. It is returning a reference to a local object that gets destroyed when the function "fillCircle1" ends. We should either not return a reference and in that case a temporary object of type Circle will be returned or a reference to a global object that does not get destroyed. Compiling the above code produces a warning:
arg1.cpp: In function �Circle& fillCircle1(Circle)’: arg1.cpp:37:11: warning: reference to local variable �object2’ returned [-Wreturn-local-addr] 37 | return object2 ; | ^~~~~~~ arg1.cpp:31:12: note: declared here 31 | Circle object2 ;
Pointers to structures.
Ex:
#include <iostream>using namespace std ;struct Circle { double radius ; double diameter ; double area ; };int main(){ Circle object1 ; Circle* ptrObject1 ; Circle* ptrObject2 ; object1.radius = 2 ; ptrObject1 = &object1 ; ptrObject2 = new Circle() ; cout << ptrObject1->radius << " : " << ptrObject2->radius << endl ; return(0) ;}
Output:
[amittal@hills Structures]$ g++ c13.cpp
[amittal@hills Structures]$ ./a.out
2 : 0
We can have pointers to structures. The notation for accessing the data members for a pointer is the symbol "->" .
Ex:
#include <iostream>using namespace std ;/*Passing Structure objects as arguments*/struct Circle { double radius ; double diameter ; double area ; };int main(){ Circle object1 ; Circle* ptrObject1 ; Circle* ptrObject2 ; object1.radius = 2 ; ptrObject1 = &object1 ; ptrObject2 = new Circle() ; cout << ptrObject1->radius << " : " << ptrObject2->radius << endl ; ptrObject2->radius = 10 ; cout << (*ptrObject2).radius << endl ; return(0) ;}
Output:
[amittal@hills Structures]$ ./a.out
2 : 0
10
The above example shows the syntax:
cout << (*ptrObject2).radius << endl ;
This is another way of accessing the data member for a pointer. The notation (*ptrObject2) will give us the object Circle and we can access the data member in the regular way of using the dot symbol "." .
Unions
A union is like a structure except that all the data members occupy the same location in RAM.
Ex:
#include <iostream>using namespace std ;//-----------------------------------union test { char ch ; int x1 ;};//-----------------------------------int main(){ test unionObject ; unionObject.x1 = 32771 ; cout << unionObject.x1 << " : " << (int) unionObject.ch << endl ; return(0) ;}//-----------------------------------
Output:
[amittal@hills union]$ ./a.out
32771 : 3
The union "test" contains 2 data member variables "ch" and "x1" . In RAM the union will look like:
RAM
---------
ch x1 unionObject
Byte1
Byte2
Byte3
Byte4
The amount of memory located is for the largest data variable in the union. Our union "test" has 2 data variables: "x1" and "ch" . An int occupies 4 bytes and a character occupies 1 byte so the memory allocated is 4 bytes. Now what sets Union apart is that both "ch" and "x1" share the same memory ! Both start from Byte1. Of course the character ch will only occupy "Byte1" and the int "x1" will occupy all the4 bytes. Let's assume our CPU is Intel which it is on the machine where this code was compiled and thus the integer is stored from lowest byte to highest byte.
We are storing the number 32771. Let's see what this number looks like in 4 bytes.
In hexadecimal:
00 00 80 03
When the bytes are arranged in RAM they show up as:
03
80
00
00
We can see that the byte value of 3 is stored in Byte1 .
cout << unionObject.x1 << " : " << (int) unionObject.ch << endl ;
We can see from the output that the value of "unionObject.ch" equals 3 .
https://www.doc.ic.ac.uk/~eedwards/compsys/memory/index.html
https://www.geeksforgeeks.org/data-structure-alignment/
Exercises:
1) How much space is allocated for the below union. Confirm that by using the "sizeof" function.
#include <stdio.h>union empAdd {char *ename;char stname[20];int pincode;};
2)
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
struct Employee
{
int empNumber; // Employee number
double hours; // Hours worked
double payRate; // Hourly payRate
};
int main()
{
Employee emp1 = { 1, 10 , 5 } ;
// Create a second employee object and initialize it with emp2 with id 2
// hours 20 and payrate 6
//Print pay of emp1 and pay of emp2. Pay is hours * payRate
}
Solutions:
1)
20
#include <stdio.h>union empAdd { char *ename; char stname[20]; int pincode;};int main(){ empAdd e1 ; printf( "%d\n" , sizeof( e1 ) ) ; }
Output:
./a.exe
20