quote:
Are you saying that
for() {
char buffer[80];
// do stuff to buffer
}
will execute at the same speed as
char buffer[80];
for() {
// do stuff to buffer
}
Yes. There's only one buffer in the function. Since they built-in type char has no constructor, there's no initialization. These two execute with the same number of instructions.
quote:
You also mentioned that "all variables that are created on the stack have their memory allocated at the same time." When exactly is the memory on the stack allocated for a local variable? For instance, if you have...*snip*
(Disclaimer: I don't make it a habit to keep one window open with the disassembly, so forgive me if some of the minor details are inaccurate.)
Then answer is at the beginning of the function. You have to look at what the compiler does at a very low level to understand why.
When you call a function in C (and I believe C++), the compiler generates assembly calls that push each argument to the function onto the stack, and then jumps to the function by modifying the instruction pointer (IP).
The first thing the function does is allocate room on the stack for all local variables in that function. In C, this was really obvious, because you HAD to declare all of your locals at the top of the function before the first statement. This is an indicator of why C is a lower-level language than C++: the syntax of the language very closely resembles what the compiler is going to churn out in assembly.
The compiler's job is to check the function and add up all the sizes of all the local variables. Think of it as saying, "I'm going to go through and add up the sizeof each local object declared in this function in every single control path". When the function starts, it just moves the stack pointer backward by this amount. It's now reserved that much space on the stack for all the local variables to exist.
Another thing it does is replace every single one of your variables with its location in memory in reference to the base pointer. Each of these you can think of as a pointer to the reserved block of memory on the stack we got at the beginning of the function call.
Look at this simple function "func" and it's related source listing:
void func (){ int x; x = 5; int* y; y = &x double z; z = 3.14;}// source listing portionPUBLIC ?func@@YAXXZ ; funcEXTRN __fltused:NEAR; COMDAT ?func@@YAXXZ_TEXT SEGMENT_x$ = -4_y$ = -8_z$ = -16?func@@YAXXZ PROC NEAR ; func, COMDAT; 2 : { push ebp mov ebp, esp sub esp, 80 ; 00000050H push ebx push esi push edi; 3 : int x;; 4 : x = 5; mov DWORD PTR _x$[ebp], 5; 5 : int* y;; 6 : y = &x lea eax, DWORD PTR _x$[ebp] mov DWORD PTR _y$[ebp], eax; 7 : double z;; 8 : z = 3.14; mov DWORD PTR _z$[ebp], 1374389535 ; 51eb851fH mov DWORD PTR _z$[ebp+4], 1074339512 ; 40091eb8H; 9 : } pop edi pop esi pop ebx mov esp, ebp pop ebp ret 0?func@@YAXXZ ENDP ; func_TEXT ENDS
Notice x, y, and z (_x$, _y$, etc.) are defined as pointers with negative value. It's counting back from the base pointer (ebp) to find these variables.
The first thing the function does is save the previous value of the base pointer (ebp), move the stack pointer (esp) into the base pointer--this will be our point-of-reference to local variables--and then reserve 80 bytes on the stack. I'm not sure why it uses 80 when it only needs 28 bytes to take care of our locals--maybe somebody here knows?
Regardless, those three lines, and the way x, y, and z are defined, allocated memory for all those variables at once. Even though they're not all declared at the same time, that one instruction (sub esp, 80) allocates all the memory needed for all local variables.
Then, it pushes all the registers it's going to use to save their values. Finally, it executes the first statement, x = 5, with "mov DWORD PTR _x$[ebp], 5". As you can see, x exists as a negative offset from the base pointer. And the rest of the function you should be able to piece together yourself.
So, stack variables don't cost you anything in memory allocation, even in loops. However, these are all standard types. If any of these were user types (i.e. user-defined structs or classes), and that type had a construction, you would see a call to the constructor whenever the statement that creates the type exists.
I hope you can see now that it takes the same amount of time to allocate an int[1] and and int[1000] on the stack, that declaring an array inside a loop doesn't cost anything, but that you pay cost of the call to a constructor every single time it's declared in your code, whether the object being constructed is on the stack or on the heap.
Edited by - Stoffel on October 18, 2000 6:15:45 PM