STORAGE Aims
To enable
students to: ·
handle
pointers ·
use
memory allocation routines ·
understand
storage classes Handle pointers
To date
variables have been viewed as a convenient way of naming a particular location
in memory. When referring to a variable access is made to its contents, for
example if an int variable is created called iTemperature, the process of assigning it the value
25 results in the number 25 ending up in some memory location -
we are not interested in which location, just that the action has occurred. In
programming it is frequently useful to divorce code from specific memory
locations. For example we may have an array of characters representing a
persons name and wish to write code that will generate a number from it by
ORing each character together. This could be written using array access
operators [] or pointers. A pointer is a
special variable whose contents is the address of another variable (including
objects), and are declared using the operator *. Given an
array of characters in memory, defined by: char
szName [7]; and
represented in memory as:
A pointer
to a character can be declared that points to the second element of the array
using the following syntax: char
*ptrcChar = &szName [1]; Note the
use of the & to obtain the address of the
variable szName[1], in this case location 1. It is easy
to modify the variable pointed to by ptrcChar as follows: *ptrcChar
= szName [4]; Note the
use of * to de-reference the pointer ptrcChar.This permits access to the memory location pointed to by the pointer (ptrcChar) - in this example the character
stored at element four of array szName (‘d’) is copied to element one, leading
to the following memory representation:
As pointers
are just another example of a variable it is possible to carry out arithmetic
operations on them. When declaring a pointer the compiler is told what type of
data object it points to, in this example a char. When applying arithmetic operators to a pointer the compiler works in
units of whatever data type is pointed to. For example, ptrcChar points to a character, incrementing
this variable by one results in ptrcChar containing its current value incremented by the size of a char (1 byte) - i.e. it now points to
the next byte in memory. Note, there is no guarantee that what actually exists
at this new location is valid data - it could be data of another type or part
of the program code. The following lines makes ptrcChar point to element two of the array: char
*ptrcChar = &szName [1]; ptrcChar
= ptrcChar + 1; The example
manipulation function can be written as: void
main () { char szName [7] = “Freddy”;// Assign the string Freddy to the array.
This // form of
assignment is only available when the // array is
created - it can not be used later. char *ptrcChar = &szName [0];// Declare a pointer to a character and
point it to // the first element of the array
szName int iValue = 0;// An integer holding the magic value
while (*ptrcChar)// End the loop when the NULL character is reached { iValue = iValue | *ptrcChar; // Or
current character value with the magic number ptrcChar ++;// Move on to the next character in the array } } Besides
pointing to basic data types, pointers can be declared to point to any user
defined type (including classes). The following code makes use of a pointer to
an object of type Clocn; #include
“locn.h”// Include
file were class prototype is defined void
vManipulatePosition (Clocn *pLocn) { pLocn->set (10, 20);// Note use of the -> access operator as we are //
dealing with a class, not a basic data type } void
main () { Clocn position;// Declare an object of type Clocn; vManipulatePosition (&position);// Pass address of object to function. } C++ defines
a special pointer value called NULL - this evaluates to zero. It is
guaranteed to be unequal to a pointer to any valid object, variable, etc. The
value of NULL is frequently used to mark the end of a list of pointers in
memory, indicate the failure of a function that returns a pointer, etc. Use memory
allocation routines
All the
variables met so far have been defined at compilation time, and consequently
exist on the function stack. The stack is an area of memory used by functions
to store local variables, pass parameter values and store the address to return
to once its execution is complete. When a function is called its parameters are
pushed onto the top of the stack. As it begins execution the function places
all its local variables, etc. onto the top of the stack. When it in turn calls
another function the parameters for the new function together with the return
address are placed on to the stack prior to the function call being made. The
new function begins execution and pushes its own values onto the top of the
stack. When it finishes execution the values it added, are popped off the top
of the stack. The next function finishes execution and the values it pushed on
to the stack are popped off, etc. A good analogy is a stack of plates in a
canteen. Variables
declared using the mechanisms met so far are placed on the stack. The following
code will result in the variable iAge being placed on the stack. void
main() { int iAge; } In many
machines stack space is limited. A second area of memory, referred to as the
heap, is used to store large items of data or those that need to persist
between function calls. To make use
of the heap C++ introduces the keywords new and delete.
To add an integer to the heap the following code would be used: void
main () { int *ptriAge; iAge = new int; } and to
destroy it: void
main () { int *ptriAge; ptriAge = new int; delete ptriAge; } The new and delete operators both work with pointers, new passing back a pointer to the
desired object within the heap and delete taking a pointer to an object within the heap to destroy. The new and delete operators are not restricted to working with basic types.
They can also handle any user defined types, for example an instance of the Clocn class can be declared as follows: #include
“locn.h”// Include
file were class prototype is defined void
main () { Clocn *ptrPosition;// Declare a pointer to an
object of type Clocn; ptrPosition = new Clocn (10, 20); //
Declare a new Clocn object with initial values // of 10
and 20 on the heap storing its location // in
ptrPosition delete ptrPosition;// Destroy the object pointed
to by ptrPosition. } If an array
of objects is required the [] are
employed, as follows: #include
“locn.h”// Include
file were class prototype is defined void
main () { Clocn *ptrPosition;// Declare a pointer to an
object of type Clocn; ptrPosition = new Clocn [10];// Declare ten new Clocn objects on the
heap // storing the location of the
first in //
ptrPosition. delete [] ptrPosition;// Destroy the ten objects on the
heap pointed to // by
ptrPosition. } Note, when
creating arrays of objects on the heap it is imperative when deleting them to
include the [], otherwise only the first object
pointed to by the variable passed to delete will be destroyed. Understand storage
classes
The data
storage class of a variable controls both its lifetime and visibility - i.e.
from where in the program it can be seen and atwhat point in execution it is created and destroyed. Four data
storage classes exist for variables that are not members of objects, auto, static, extern and const. The storage
class of a variable is given before its type, for example: { auto int iAge; static float fHeight; const auto iDept = 10; … float fShoeSize; // Note, because the
storage type is not specified this variable // is assumed to be auto. } By default
variables are of auto type. This means that the variable
has local scope to the current program block - it can not be seen by
surrounding code blocks or other functions. When the program finishes executing
the current block of code the variable is destroyed. Variables
defined as const can not have their value changed. An external
variable is declared outside a code block, i.e. outside of a function are
available to any code within that source file and exist for the lifetime of the
program. For example in the following code the variable iLongLife is available within the main
function (and any others defined within that file) and exists while the program
is executing. int
iLongLife; void
main () { iLongLife = 99; cout << iLongLife; } To permit
one source file to access an external variable defined by another source the extern keyword is used. For example, two
source files first.cpp and second.cpp are written, one declaring an external
variable and the other accessing it. //
first.cpp int
iValue;// Declare an external
variable void
vModify (int iNewValue) { iValue = iNewValue; } ---------------------------------End of first file
--------------------------------- //
second.cpp extern
int iValue;// Access an external
variable declared elsewhere void
vRevert (int iOldValue) { iValue = iOldValue; } Both
external variables and the extern
keyword should be avoided if possible - they can lead to unpredictable program
behaviour especially in large projects where source files beyond your control
may change a variable. Variables
proceeded by the static keyword have what are termed static
duration - they are allocated when the program begins and destroyed when the
program ends, not when the code block they are defined in is entered and left.
In this respect they are similar to external variables, nut unlike them they
are only visible within the program block where their declaration is made. As
an example in the following code the variable iSomeValue is created when the program starts and destroyed when it
ends, not when the function vSomeFunction is entered and left. void
vSomeFunction () { static int iSomeValue; iSomeValue = 27; } All
variables preceded by the static keyword are initialised to zero in the absence
of some other specified value. void
vTesting () { static int iSomeValue;// Has the value of 0 static int iAnotherValue = 33;// Has the value of 33 } External
variables are obviously very useful - if they can be controlled. Within a
single source files it is easy to police changes to a variables value. An
external variable can be restricted to use within a single source file via the static keyword. External variables
declared as static can not be accessed within other
files via the extern keyword. Member
variables of classes can also use storage class modifiers, as in: class
CVehicle { public: auto char szRegistrationNumber [10]; static int iVehicleCount; CVehicle () { iVehicleCount++; } ~CVehicle () { iVehicleCount--; } void setRegistrationNumber (char
*szRegistrationIn) { strncpy (szRegistrationNumber,
szRegistrationIn); } } In the
class CVehicle two member variables are defined,
one auto and the other static - as usual variables without a
class modifier are assumed to be auto. Each object
of a class has its own set of auto
variables, but any static variables are shared between all
the objects. The auto member variables of an object are
created whenever it is instantiated, and deleted whenever the object is
destroyed. Because static members are not associated with a
particular object, but instead are common to all, they are not created when an
instance of an object is instantiated. Instead, the programmer must create the static member variables of a class before
an object of its type is instantiated - as shown below: #include
“verhicle.h” int
CVehicle::iVehicleCount = 0; // Allocate an area in memory for the static
member // variable
iVehicleCount void
main () { CVehicle newVehicle; } Note,
because static variables are automatically assigned the value of 0 in the
absence of any other value the line allocating memory space for iVehicleCount could be rewritten as: int
CVehicle::iVehicleCount; The class Vehicle gives an example of a possible use
for a static variable. In this example iVehicleCount is incremented by the constructor and decremented by the
destructor - it acts as a count of the number of objects of type Vehicle that have been created. At any
point the public variable iVehicleCount of any object may be interrogated to find out how many instances of
that class type exist. Because static
variables are not tied to an individual object but instead exist independently
in memory it is also possible to directly refer to CVehcile::iVehicleCount and obtain the same value. As an
example: #include
“vehicle.h” int
CVehicle::iVehicleCount = 0; void
main () { CVehicle vehicle1;// Increment
CVehcile::iVehicleCount cout << vehicle1.iVehicleCount;// Produces the answer 1 Vehicle vehicle2;// Increment Vehcile::iVehicleCount cout << CVehicle::iVehicleCount; //
Produces the answer 2 } |
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Last updated: 11th July 2006. copyright © 2006 Greystoke Systems Ltd. Web address: http://www.gsys.biz/Documents/Services/Tuition/CityAndGuilds/7261-249/Storage.htm |