FM> I found that I COULD bring up the addresses of elements of a
FM> character array
Yep. And if you are using iostream, just cast the address to a
void* and you can output the actual values of the addresses.
FM> but then at the same time I tested something else I
FM> had heard was true - that the external variables went on the heap
FM> while the internal ones went on the stack.
There are no requirements that variables be kept on the stack or on
the heap or anywhere else. There's not even a requirement that
a system _have_ a stack or a heap. But most commonly, local variables
get storage on the stack. Static variables (including globals) get
stored in some memory location that has program length duration.
Perhaps the system's startup code may call some OS routine to get
a hunk of the system heap for static variables. Perhaps not.
It shouldn't matter as far as how you (or I!) write programs.
FM> But I found that these
FM> array element addresses were numbered downward just like the
FM> addresses of my internal variables, declared within main().
FM> What am I not seeing here?
You're eyesight is excellent. What you _are_seeing_ is implementation
defined behavior!
That automatic variables (local variables typically stored on the
stack) can have increasing addresses comes from how the memory is
allocated. Local variables are not pushed onto the stack. That
could cause a heavy performance hit.
Consider something like...
void foo()
{
short i;
char c;
long j;
DoSomething();
return;
}
When this gets compiled, there is procedure entry code and proc exit
code added. (This is part of what is called "procedure call overhead".)
The compiler knows how much space it needs for local variables. So at
proc entry, it can simply decrement from the stack pointer that many bytes.
Then those bytes are "safe". The compiler can then map the local vars
to appropriate slots in that block of memory. It could assign them
from the old SP up or from the decremented SP down. It doesn't matter.
In fact, there's no guarantee that the memory space for one variable
will directly follow the memory space for the preceedingly declared
variable. (Though there is a guranteed order for members in a
struct.)
Here's what you are guaranteed from a variable. Let's use an
array of chars for example.
char buffer[2];
You are guaranteed that you can safely dereference pointers to
&buffer[0] and &buffer[1]. You can also safely get the address
of one past the end of the array. But you cannot safely dereference
a pointer at one past the end, nor can you safely even get the
address of more than one past the end or one from the beginning.
Some of this is useless esoterica. But then again, this concept is
important for some situations like iterators in collection classes
The STL uses this concept in it's begin() and end() members in the
container classes. end() returns "one past the end".
---
þ Blue Wave/QWK v2.12 þ
---------------
* Origin: St. Louis Users Group (1:100/4)
|