Computers store programs in several different chunks of memory.
Data
There is the "data" block, where all constant values are stored. For example, in
1 2
|
string s = "Hello world!";
|
the ASCIIZ character sequence "
Hello world!" is stored in the data segment.
Text
There is the "text" or "code" block, where the code that makes things happen is stored. For example:
1 2 3 4 5
|
int main()
{
cout << "Hello world!\n";
return 0;
}
|
Everything that
does something -- computer instructions -- is put in the text block. (Remember that the character string goes in the data block.)
Stack
There is the "stack", which is used for temporary variables and information about where to
return to when a function terminates, etc. The stack is so called because it is a LIFO structure.
1 2 3 4 5 6 7
|
template <typename ValueType>
void swap( ValueType& a, ValueType& b )
{
ValueType c = a;
a = b;
b = c;
}
|
In this example, the variables
a,
b, and
c are all stored on the stack. (Remember that a
reference is a pointer in disguise, so
a and
b are actually pointers to
ValueType, but
c is actually a full-blown instance of
ValueType.)
when the function returns, it calls
c's destructor, goes back to from where the function was called, and restores the stack to its previous state.
Heap
Finally, there is the "heap", which is free (or unused) space that the program can use for dynamic memory allocations.
|
int* numbers = new int[ 100 ];
|
Remember, if
numbers is in a function, it is stored on the stack. But the integers it points to are all stored somewhere in the heap.
new and
delete (and
malloc() and
free()) manage the parceling of the heap.
So, what does it all mean?
Typically, a language will use the heap to allocate memory for class type objects.
This has the advantage that code that uses the object only needs to worry about its local pointer (or reference) to that object. For example, in
Delphi Pascal, an object is created thus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
type
tGreeting = class
public
procedure say_hello;
end;
procedure tGreeting.say_hello;
begin
writeln( 'Hello world!' )
end;
var greet: tGreeting;
begin
new( greet ); // create a tGreeting on the heap and assign its address to 'greet'
greet.say_hello; // use the object's methods
dispose( greet ) // destroy the object and return its memory back to the heap
end.
|
The variable
greet is just a pointer (or "reference") to the object, and all the stack space that is needed for it is the size of a pointer.
The disadvantage is that you must always explicitly
new and
dispose every object you use.
C++, however, has the friendly feature that you can create and treat an object as if it were a local variable: on the stack and completely ignoring the heap. (The object may use the heap, but the object itself is stored on the stack.) This means that you can do things like create local, nameless objects:
1 2 3 4 5 6
|
void foo( string& s ) { cout << s; }
int main()
{
foo( string( "Hello world!" ) );
return 0;
}
|
The string passed to
foo() as argument only exists on the stack, and it is automatically destroyed when after
foo() is done with it.
The disadvantage is that now the code that creates the local object needs to know how much space the object uses on the stack, where local variables are kept. Meaning that if you change the object's size the function that uses it must be recompiled to accommodate the new size.
Whew. Hope this helps.