Pointers And Memory - Contents
A D V E R T I S E M E N T
Local Memory:
The most common variables you use are "local" variables within functions such as the
variables num and result in the following function. All of the local variables and
parameters taken together are called its "local storage" or just its "locals", such as num
and result in the following code...
// Local storage example
int Square(int num) {
int result;
result = num * num;
return result;
}
The variables are called "local" to capture the idea that their lifetime is tied to the
function where they are declared. Whenever the function runs, its local variables are
allocated. When the function exits, its locals are deallocated. For the above example, that
means that when the Square() function is called, local storage is allocated for num and
result. Statements like result = num * num; in the function use the local
storage. When the function finally exits, its local storage is deallocated
Here is a more detailed version of the rules of local storage...
1. When a function is called, memory is allocated for all of its locals. In other
words, when the flow of control hits the starting '{' for the function, all of
its locals are allocated memory. Parameters such as num and local
variables such as result in the above example both count as locals. The
only difference between parameters and local variables is that parameters
start out with a value copied from the caller while local variables start with
random initial values. This article mostly uses simple int variables for its
examples, however local allocation works for any type: structs, arrays...
these can all be allocated locally.
2. The memory for the locals continues to be allocated so long as the thread
of control is within the owning function. Locals continue to exist even if
the function temporarily passes off the thread of control by calling another
function. The locals exist undisturbed through all of this.
3. Finally, when the function finishes and exits, its locals are deallocated.
This makes sense in a way � suppose the locals were somehow to
continue to exist � how could the code even refer to them? The names
like num and result only make sense within the body of Square()
anyway. Once the flow of control leaves that body, there is no way to refer
to the locals even if they were allocated. That locals are available
("scoped") only within their owning function is known as "lexical
scoping" and pretty much all languages do it that way now.
Small Locals Example:
Here is a simple example of the lifetime of local storage...
void Foo(int a) { // (1) Locals (a, b, i, scores) allocated when Foo
runs
int i;// This array of 100 floats is allocated locally.
float scores[100];// (2) Local storage is used by the computation
for (i=0; i
Bar(i + a); // (3) Locals continue to exist undisturbed,
} // even during calls to other functions.
} // (4) The locals are all deallocated when the function exits.
Large Locals Example:
Here is a larger example which shows how the simple rule "the locals are allocated when
their function begins running and are deallocated when it exits" can build more complex
behavior. You will need a firm grasp of how local allocation works to understand the
material in sections 3 and 4 later.
The drawing shows the sequence of allocations and deallocations which result when the
function X() calls the function Y() twice. The points in time T1, T2, etc. are marked in
the code and the state of memory at that time is shown in the drawing
void X() {
int a = 1;
int b = 2;
// T1
Y(a);
// T3
Y(b);
// T5
}
void Y(int p) {
int q;
q = p + 2;
// T2 (first time through), T4 (second time through)
}
Observations About Local Parameters:
Local variables are tightly associated with their function � they are used there and
nowhere else. Only the X() code can refer to its a and b. Only the Y() code can refer to
its p and q. This independence of local storage is the root cause of both its advantages
and disadvantages.
Advantages Of Locals:
Locals are great for 90% of a program's memory needs....
- Convenient: Locals satisfy a convenient need � functions often need
some temporary memory which exists only during the function's
computation. Local variables conveniently provide this sort of temporary,
independent memory.
- Efficient:
Relative to other memory use techniques, locals are very
efficient. Allocating and deallocating them is time efficient (fast) and they
are space efficient in the way they use and recycle memory
- Local Copies:
Local parameters are basically local copies of the
information from the caller. This is also known as "pass by value."
Parameters are local variables which are initialized with an assignment (=)
operation from the caller. The caller is not "sharing" the parameter value
with the callee in the pointer sense� the callee is getting its own copy.
This has the advantage that the callee can change its local copy without
affecting the caller. (Such as with the "p" parameter in the above
example.) This independence is good since it keeps the operation of the
caller and callee functions separate which follows the rules of good
software engineering � keep separate components as independent as
possible.
Disadvantages Of Locals:
There are two disadvantages of Locals
- Short Lifetime:
Their allocation and deallocation schedule (their
"lifetime") is very strict. Sometimes a program needs memory which
continues to be allocated even after the function which originally allocated
it has exited. Local variables will not work since they are deallocated
automatically when their owning function exits. This problem will be
solved later in Section 4 with "heap" memory.
- Restricted Communication:
Since locals are copies of the caller
parameters, they do not provide a means of communication from the callee
back to the caller. This is the downside of the "independence" advantage.
Also, sometimes making copies of a value is undesirable for other reasons.
We will see the solution to this problem below in Section 3 "Reference
Parameters".
Synonyms For "Local":
Local variables are also known as "automatic" variables since their allocation and
deallocation is done automatically as part of the function call mechanism. Local variables
are also sometimes known as "stack" variables because, at a low level, languages almost
always implement local variables using a stack structure in memory.
The Ampersand (&) Bug � TAB:
Now that you understand the allocation schedule of locals, you can appreciate one of the
more ugly bugs possible in C and C++. What is wrong with the following code where the
function Victim() calls the function TAB()? To see the problem, it may be useful to make
a drawing to trace the local storage of the two functions...
// TAB -- The Ampersand Bug function
// Returns a pointer to an int
int* TAB() {
int temp;
return(&temp); // return a pointer to the local int
}
void Victim() {
int* ptr;
ptr = TAB();
*ptr = 42; // Runtime error! The pointee was local to TAB
}
TAB() is actually fine while it is running. The problem happens to its caller after TAB()
exits. TAB() returns a pointer to an int, but where is that int allocated? The problem is
that the local int, temp, is allocated only while TAB() is running. When TAB() exits,
all of its locals are deallocated. So the caller is left with a pointer to a deallocated
variable. TAB()'s locals are deallocated when it exits, just as happened to the locals for
Y() in the previous example.
It is incorrect (and useless) for TAB() to return a pointer to memory which is about to be
deallocated. We are essentially running into the "lifetime" constraint of local variables.
We want the int to exist, but it gets deallocated automatically. Not all uses of & between
functions are incorrect � only when used to pass a pointer back to the caller. The correct
uses of & are discussed in section 3, and the way to pass a pointer back to the caller is
shown in section 4.
Local Memory Summary:
Locals are very convenient for what they do � providing convenient and efficient
memory for a function which exists only so long as the function is executing. Locals have
two deficiencies which we will address in the following sections � how a function can
communicate back to its caller (Section 3), and how a function can allocate separate
memory with a less constrained lifetime (section 4).
Back to Table of Contents
A D V E R T I S E M E N T
|
Subscribe to SourceCodesWorld - Techies Talk |
|