When performing reverse engineering on a software created by a compiler such as C or C++, we can see how the stack is manipulated.
Before calling a function, each parameter value is pushed on the stack. The function is then called with the instruction "CALL" which perform two operations : it push the current instruction pointer on the stack, then perform a "JUMP" by over-writing the instruction pointer with the address of the function.
The first operation performed when entering a function is to add a specific constant to the stack pointer. The constant is equal to the number of bytes needed by all the local variables. Adding a constant is like performing a number of pushes all at once. This is a very efficient way to reserve a frame on the stack. However, it does mean that local variables are not initialized before they are first used.
Every part of the function which needs to access a local variable will use an indexed instruction. In other words, it can read/write any variable without first pop-ing any value from the stack.
In the case of x86 processor, since it lacks instructions to access the stack pointer, the convention is to copy the value of the stack pointer to another register, EBP, which does have indexing address mode. The register EBP is copied before reserving the space for the local variables. The beauty of this convention is that all parameters are using positive index values and all local variables are using negatives values.
Final remark: when a compiler uses aggressive optimization techniques, the reverse engineered code may be very hard to understand. For example, if a local variable is used only near the top of a function, the compiler may pop it out. After executing that line, the index value used is adjusted to take account of the updated stack pointer. Another optimization which makes lots of sense is for the compiler to use a register for some local variable instead of reserving space on the stack. The reverse engineered code would show that the index used on the stack would effectively have values not assigned to some local variables.