From: Thomas J. Gritzan on
Am 03.06.2010 21:04, schrieb RB:
> Thanks to all of you who replied, just now getting back online.
> I originally posted since I was having trouble getting a breakpoint to
> hold anywhere that I could get a look at the difference between a
> Global and Local instance. I later created a small console app
> (shown below) where I was able to actually see the two scenrarios
> being created in dissassembly.
> But I'm glad I posted anyhow since you answered my confusion
> and there is no way that I know of to actually find out the size of the
> stack, I was only able to decipher what "appeared" to be created on
> the heap.
> Below is the app I experiemented with. My assembler is very rusty
> but I pulled out my old book and I believe I have the // comments
> correct.

see below..

> -----------------------------------------------------------------
> class Class
> {
> public:
> int G;
> char GString[14];
>
> // Construction
> Class()
> {
> G = 0xAAAAAAAA;
> int ct = 0;
> while(ct < 14)
> {
> GString[ct] = 'S';
> ct++;
> }
> GString[ct] = '\0';

Here you access the 14th element of GString, but GString only has the
elements 0...13.
Also, the magic number 14 is a bad idea, because if you change the
length of the string, you'll forget to change the while loop.

Better use a std::string instead.

[...]
> -------now the view into the construction of each
> -------starting with global object construction
[...]
> 004010CD 8B 45 FC mov eax,dword ptr [ebp-4] //mov offset in eax
> 004010D0 C7 00 AA AA AA AA mov dword ptr [eax],0AAAAAAAAh
> // moved val into addr ptr setting in eax, Class int G has just been initialized
> // to 4 bytes of Ah. However the addr value is 427D50h and the current stack
> // is 12FECCh thru 12FE84h so it would appear that Class in not on the stack
> // but on the heap, since is (427D50h - 12FE84h) = 3112652d or
> // (2F7ECCh)bytes above current stack and the stack grows down.

You assume that there are only two kinds of memory. However, global
objects exist neither on the heap nor on the stack. Global variables are
declared in the data section of the executable and are loaded by the PE
loader before the heap exists and before any user code runs.

--
Thomas
From: RB on
> Here you access the 14th element of GString, but GString only has the
> elements 0...13.
> Better use a std::string instead.

You are correct, I got the ct wrong. I normally use a string copy routine
but I did not know for sure what would be loaded at the time of global
construction.

> You assume that there are only two kinds of memory. However, global
> objects exist neither on the heap nor on the stack. Global variables are
> declared in the data section of the executable and are loaded by the PE
> loader before the heap exists and before any user code runs.

Assume would be correct if I was that smart, but I really did not have
a clue. I guess it would be up to the compiler what ever it wanted to
use for an object creation.
Thanks for the reply and informative information. By the way is there
any way to actually find out where a runtime stack starts and what it's
size is ?


From: Joseph M. Newcomer on
See below...
On Thu, 3 Jun 2010 15:04:30 -0400, "RB" <NoMail(a)NoSpam> wrote:

>Thanks to all of you who replied, just now getting back online.
>I originally posted since I was having trouble getting a breakpoint to
>hold anywhere that I could get a look at the difference between a
>Global and Local instance. I later created a small console app
>(shown below) where I was able to actually see the two scenrarios
>being created in dissassembly.
> But I'm glad I posted anyhow since you answered my confusion
>and there is no way that I know of to actually find out the size of the
>stack, I was only able to decipher what "appeared" to be created on
>the heap.
> Below is the app I experiemented with. My assembler is very rusty
>but I pulled out my old book and I believe I have the // comments
>correct.
>-----------------------------------------------------------------
>class Class
>{
>public:
> int G;
> char GString[14];
>
>// Construction
>Class()
> {
> G = 0xAAAAAAAA;
> int ct = 0;
> while(ct < 14)
> {
> GString[ct] = 'S';
> ct++;
> }
> GString[ct] = '\0';
> }
>
>// Destruction
>~Class()
> {
> }
>}; // end of Class implemetation
>
>//Global objects
> Class Global;
>
>void main(void)
>{
> Class Local;
>}
>-------now the view into the construction of each
>-------starting with global object construction
>5: class Class
>6: {
>7: public:
>8: int G;
>9: char GString[14];
>10:
>11: // Construction
>12: Class() //arriving into the constructor code
>004010B0 55 push ebp //save stack state again
****
This is saving the old *frame pointer* on the stack
****
>004010B1 8B EC mov ebp,esp
****
This establishes the new *frame pointer*

The *frame pointer* is a fixed point by which parameters and local variables are
addressed. Parameters are a fixed positive distance from the frame pointer and
local variables are a fixed negative offset from the frame pointer. This concept is not
used if you use "frame pointer optimization" or in 64-bit code.

int f(int p1, int p2, int p3)
{
int L1;
int L2;
int L3;

Call seqence
push p3
push p2
push p1
call f
add esp, 12

Stack at function entry

ebp---> frame pointer of caller

stack after two above instructions
p3 [16+ebp]
p2 [12+ebp]
p1 [8+ebp]
ret [4+ebp]
ebp--> old ebp

>004010B3 83 EC 48 sub esp,48h //allocate 48h bytes of stack
****
After this, the esp has been decreased by a certain amount

p3 [16+ebp]
p2 [12+ebp]
p1 [8+ebp]
ret [4+ebp]
ebp--> old ebp
///...n bytes of buffer overrun detection area...///
L1 [n-4+ebp]
L2 [n-8+ebp]
L3 [n-12+ebp]
Note that the "n bytes of buffer overrun detection" area is why even for a small function
you see values like 48h (72) for what is a function of very few local variables (actually,
this probably means two integer or pointer locals, and 64 (40h) bytes of buffer overrun
space)
****
>004010B6 53 push ebx //save regs
>004010B7 56 push esi
>004010B8 57 push edi
>004010B9 51 push ecx //save offset of class on stack
****
This is a __thiscall function, a nonstatic method of a class. In __thiscall functions,
the 'this' pointer is always in the ecx register
****
>004010BA 8D 7D B8 lea edi,[ebp-48h] //grab addr into edi
****
This is the second local variable declared
****
>004010BD B9 12 00 00 00 mov ecx,12h //num of rep's
****
rep stos will repeat as long as ecx is nonzero, and will decrement it
***
>004010C2 B8 CC CC CC CC mov eax,0CCCCCCCCh //val to initialize
****
This is setting up to fill in 4*12h bytes of stack space to 0xCCCCCCCC which is the
default initialization of all local variables in debug mode
****
>004010C7 F3 AB rep stos dword ptr [edi] //do the stos
****
By saying "dword ptr" it means that this stos stores 32-bit values from eax into edi,
incrementing edi by 4 (sizeof(dword)), repeating as long as ecx is nonzero
****
>004010C9 59 pop ecx //retrieve class offset addr
****
Note that in release mode, where the ecx register wasn't needed to do the local
initialization, it would neither be stored nor retrieved, so everything from the push ecx
through the pop ecx would be missing.
****
>004010CA 89 4D FC mov dword ptr [ebp-4],ecx //put it in ebp-4
****
Stores the 'this' pointer in a local variable
****
>13: {
>14: G = 0xAAAAAAAA;
>004010CD 8B 45 FC mov eax,dword ptr [ebp-4] //mov offset in eax
>004010D0 C7 00 AA AA AA AA mov dword ptr [eax],0AAAAAAAAh
****
This sets the first member variable of the class to 0xAAAAAAAA.
****
> // moved val into addr ptr setting in eax, Class int G has just been initialized
> // to 4 bytes of Ah. However the addr value is 427D50h and the current stack
****
You have not shown where this class is instantiated. So you are confusing the code which
initializes an object from code that allocates the object. You did NOT show the context
from which this constructor was called, which is almost certainly from a new of either an
object of this class, or the new of an object (such as a CView-derived class or
CDocument-derived class) that contains an instance of the class Class. Since you have not
shown the code that causes the constructor to be called, telling us what the values are is
not indicative of anything much. As you point out, it almost certainly is on the heap,
but you have shown no code to indicate where it was allocated
****
> // is 12FECCh thru 12FE84h so it would appear that Class in not on the stack
> // but on the heap, since is (427D50h - 12FE84h) = 3112652d or
> // (2F7ECCh)bytes above current stack and the stack grows down.
>-----------now the local object construction
****
You are confusing the constructor with allocation. The constructor you showed does not
allocate; it is called to initialize the storage which was allocated by some other
mechanism. SInce you did not show the entire program there is no way to tell what is
going on here.
joe
****
>5: class Class
>6: {
>7: public:
>8: int G;
>9: char GString[14];
>10:
>11: // Construction
>12: Class()
>004010B0 55 push ebp //save stack state
>004010B1 8B EC mov ebp,esp //reset stack base
>004010B3 83 EC 48 sub esp,48h //allocate 48h bytes on stack
>004010B6 53 push ebx //save regs
>004010B7 56 push esi
>004010B8 57 push edi
>004010B9 51 push ecx //save offset of class on stack
>004010BA 8D 7D B8 lea edi,[ebp-48h] //loads 12FED0 in edi
>004010BD B9 12 00 00 00 mov ecx,12h // mov count for rep
>004010C2 B8 CC CC CC CC mov eax,0CCCCCCCCh // initializer value
>004010C7 F3 AB rep stos dword ptr [edi] // initialize 12h*4= 48h bytes
>004010C9 59 pop ecx // retrieve the value passed in ecx, 12FF6Ch
>004010CA 89 4D FC mov dword ptr [ebp-4],ecx // put at ebp-4
>13: {
>14: G = 0xAAAAAAAA;
>004010CD 8B 45 FC mov eax,dword ptr [ebp-4] // mov it to eax
>004010D0 C7 00 AA AA AA AA mov dword ptr [eax],0AAAAAAAAh
> // mov 4 bytes of Ah's this address which I now see is address of the
> // class int G, and it's addr is shown to be 12FF6Ch which is probably on
> // the stack and not the heap since ebp-48h is 12FED0h, which is only
> // 156d bytes away.
>
Joseph M. Newcomer [MVP]
email: newcomer(a)flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
From: Joseph M. Newcomer on
The size of the stack is determined by linker options. By default, your stack is 1MB, of
which the first 16K or so are committed to existence.

The stack starts somewhere. You never really need to know this, and there is no way to
predict it, and you shouldn't care. Note that if you call this function from a thread,
you will using that thread's stack, which is also someplace unpredictable and largely
indeterminate until the thread is actually created.

You can add runtime stack size checking, or just wait until the stack access faults. Is
there a reason you need to know the stack size?
joe


On Thu, 3 Jun 2010 21:21:30 -0400, "RB" <NoMail(a)NoSpam> wrote:

>
>> Here you access the 14th element of GString, but GString only has the
>> elements 0...13.
>> Better use a std::string instead.
>
>You are correct, I got the ct wrong. I normally use a string copy routine
>but I did not know for sure what would be loaded at the time of global
>construction.
>
>> You assume that there are only two kinds of memory. However, global
>> objects exist neither on the heap nor on the stack. Global variables are
>> declared in the data section of the executable and are loaded by the PE
>> loader before the heap exists and before any user code runs.
>
>Assume would be correct if I was that smart, but I really did not have
>a clue. I guess it would be up to the compiler what ever it wanted to
>use for an object creation.
>Thanks for the reply and informative information. By the way is there
>any way to actually find out where a runtime stack starts and what it's
>size is ?
>
Joseph M. Newcomer [MVP]
email: newcomer(a)flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
From: Doug Harrison [MVP] on
On Fri, 04 Jun 2010 00:09:14 +0200, "Thomas J. Gritzan"
<phygon_antispam(a)gmx.de> wrote:

>You assume that there are only two kinds of memory. However, global
>objects exist neither on the heap nor on the stack. Global variables are
>declared in the data section of the executable and are loaded by the PE
>loader before the heap exists and before any user code runs.

A related and perhaps more frequently useful concept to understand is
"storage duration". There are three types:

1. Automatic, which applies to non-static local variables as well as
temporaries and function parameters. These are notionally the "stack
variables".

2. Static, which applies to global variables (more generally,
namespace-scope variables), static data members of classes, and local
statics. They live in the data/bss section of the process.

3. Dynamic, which applies to objects created with new. They live on the
heap.

I don't normally care or pause for even a microsecond to think about the
memory addresses where these objects live. Instead, I think about the
lifetime of objects, which is critical to understand, and there are some
subtleties to automatic and static storage duration. OTOH, being able to
recognize memory addresses for stack, heap, and data sections is sometimes
useful when debugging, and I once needed to be able to tell if an object
with static storage duration was inside an EXE or DLL, but that was to
implement something pretty strange.

--
Doug Harrison
Visual C++ MVP