I think I might be asking a very wrong question, but I really tried to understand it by googling, but with no luck.
As we know, we have a stack and heap. Heap for the dynamically allocated ones, stack for local variables and e.t.c.
Let's say I have the following c++ code.
void bla(int v1, int v2, int v3) {
int g = v1 + v2+ v3;
}
void nice(int g){
int z = 20;
int k = 30;
bla(g, z, k);
}
int main(){
cout<<"Hello World";
nice(40);
}
Now, let's imagine there's a stack. I understand that for example values z,k,g will be stored on stack. But when I call the function nice which calls bla where are those stored ? I've read that each function execution causes call stack size to increase by 1. I'd say that even creating local variables also causes call stack to be increased by 1.
So, how are those(callstack, stack) related at all ?
Here is my assumption:
When we call nice, completely new stack gets created. In there, we store z and k. When nice calls bla , now another stack gets created for bla and this second stack stores v1,v2,v3,g. and so on. each function needs its own callstack,but we can call this stack too.
Each running process is allocated a chunk of memory which it calls "the stack." And, this area of memory is used both to represent the "call/return sequence" (through what are known as "stack frames"), and the so-called "local variables" that are used by each routine that is called. They are one and the same.
Usually, different CPU registers are used to point simultaneously to each thing. One register points to the "local variable" values, while an entirely different register points to the "stack frame." (In interpreters, a similar mechanism is used.) There's also a third register which indicates the current "top of stack."
At the beginning of executing each new function, the "top of stack" is simply moved ahead to account for the local variables, after the "local variables pointer" remembers what it used to be.
When a "return from subroutine" occurs, the "top-of-stack pointer" is reverted to some previous value, and everything that once existed "above it" is now, literally, forgotten. Because it no longer matters.
So, how are those(callstack, stack) related at all ?
They are very much related. They are the same thing. It is also called the execution stack.
This "callstack" is not to be confused with the general concept of "stack" data structure. The callstack called a stack because that describes the structure of the callstack.
causes call stack size to increase by 1
By "1" sure, but what it the unit of the increase? When you call a function, the stack pointer is incremented one stack frame. And the size (measured in bytes) of the stack frame varies. The frame of a function is big enough to contain all local variables (the parameters may also be stored on the stack).
So, if you wish to measure the increment in bytes, then it is not 1, but some number greater than or equal to 0.
I'd say that even creating local variables also causes call stack to be increased by 1.
As I described, having a local variable affects how the stack pointer is incremented when the function is called.
When we call nice, completely new stack gets created.
No, the same stack is shared by all function calls in the entire thread of execution.
Pretty much none of this is specified by the C++ language, but rather are implementation details that apply to most C++ implementations in typical case, but are simplified for easier understanding.
Stack denotes a data structure which consists of a set of usually similar items and has the following abilities:
push: adds an item to the "top" of the stack, which will become the new top
pop: removes the item from the top, thus the item which was previously the top will be the top again; this operation returns the item you remove
top: gets the top item of the stack, without modifying your stack
Your memory has a section called "the stack", where, as you correctly understood, functions are stored. That is the call stack. However, stack and call stack are not 100% equivalent, since stacks are used in many other cases, basically whenever you need a LIFO (Last In First Out) processing. It's used at the LIFO policy of the CPU, for example, or, when you do a depth-first search in a graph. In short, stack is a data structure, which can be applied in many cases, the call stack in memory being a prominent example.
So, why stack is in use to store function calls in memory. Let's take into consideration embedded function calls, like:
f1(f2(...(fn(x))...))
It's a rule of thumb that in order to evaluate fi, 1 <= i < n, you need to evaluate fj, where 1 < j <= n, assuming that i < j. As a result, f1 is the first to be called, but the last to be evaluated. Hence, you have a LIFO processing, which is best done via using a stack.
Related
I am slightly confused on how a compiler stores variable on the stack. I read that c++ can store local variables on the stack, but if the stack is LIFO, how could it make sure to call the right variable when the variable is called in the program?
The run-time stack isn't simply a LIFO structure; it's a complex structure which supports random access.
Typically how it works on many platforms is that whe na function is entered, a new stack frame is pushed onto the stack (LIFO fashion). Local variables are stored within the frame and are accessed relative to a more or less stable pointer stored in a register.
When other functions are called, they push their own frames, but they restore everything before returning.
Run-time stacks also typically support the ad hoc pushing and popping of individual values, for the purpose of temporarily saving register values or for parameter passing.
A common implementation strategy for that is to allocate the frame first. For instance if a 512 byte stack frame is needed, the stack pointer is moved by 512 bytes. The stack pointer is then used freely for pushing and popping, provided it doesn't pop too far and start gobbling into the frame.
A separate frame pointer may be used which tracks the location of the frame. The frame is then accessed relative to that frame pointer, which allows the stack pointer to move without interfering with those accesses.
Compilers can generate code without the use of frame pointers also; if the stack pointer only moves in ways that a compiler knows about, it can adjust all the variable references. Over an area of the code where the compiler knows that the stack pointer has moved by four bytes due to something being pushed on it, it can adjust the stack-pointer-relative frame references by four bytes.
The basic principle is that when a given function is executing, then the stack is in the right state that is expected by that function (unless something has gone horribly wrong due to a bug causing corruption or something). The LIFO allocation strategy of the stack frames closely tracks the function calls and returns. Functions that are called must save and restore certain registers (the "callee-saved registers"), which helps to maintain the stable stack environment.
Recently, I came across this question in an interview: How can we determine how much storage on the stack a particular function is consuming?
The "stack" is famously an implementation detail of the platform that is not inspectable or in any way queryable from within the language itself. It is essentially impossible to guarantee within any part of a C or C++ program whether it will be possible to make another function call. The "stack size", or maybe better called "function call and local variable storage depth", is one of the implementation limits whose existence is acknowledged by the language standard but considered out of scope. (E.g. for C++ see [implimits], Annex B.)
Individual platforms may offer APIs to allow programs to introspect the platform limitations, but neither C nor C++ specify that or how this should be possible.
Exceeding the implementation-defined resource limits leads to undefined behaviour, and you cannot know whether you will exceed the limits.
It's completely implementation defined - the standard does not in any way impose requirements on the possible underlying mechanisms used by a program.
On a x86 machine, one stack frame consists of a return address (4/8 byte), parameters and local variables.
The parameters, if e.g. scalars, may be passed through registers, so we can't say for sure whether they contribute to the storage taken up. The locals may be padded (and often are); We can only deduce a minimum amount of storage for these.
The only way to know for sure is to actually analyze the assembler code a compiler generates, or look at the absolute difference of the stack pointer values at runtime - before and after a particular function was called.
E.g.
#include <iostream>
void f()
{
register void* foo asm ("esp");
std::cout << foo << '\n';
}
int main()
{
register void* foo asm ("esp");
std::cout << foo << '\n';
f();
}
Now compare the outputs. GCC on Coliru gives
0x7fffbcefb410
0x7fffbcefb400
A difference of 16 bytes. (The stack grows downwards on x86.)
As stated by other answers, the program stack is a concept which is not specified within the language itself. However with a knowledge how typical implementation works, you can assume that the address of the first argument of a function is the beginning of its stack frame. The address of the first argument of a next called function is the beginning of the next stack frame. So, they probably wanted to see a code like:
void bar(void *b) {
printf("Foo stack frame is around %lld bytes\n", llabs((long long)b - (long long)&b));
}
void foo(int x) {
bar(&x);
}
The size increase of the stack, for those implementations that use a stack, is:
size of variables that don't fit in the available registers
size of variables declared in the function declared upfront that live for the life of the function
size of other local variables declared along the way or in statement blocks
the maximum stack size used by functions called by this function
everything above * the number of recursive calls
size of the return address
Return Address
Most implementations push the return address on the stack before any other data. So this address takes up space.
Available Registers
Some processors have many registers; however, only a few may be available for passing variables. For example, if the convention allows for 2 variables but there are 5 parameters, 3 parameters will be placed on the stack.
When large objects are passed by value, they will take up space on the stack.
Function Local Variables
This is tricky to calculate, because variables may be pushed onto the stack and then popped off when not used.
Some variables may not be pushed onto the stack until they are declared. So if a function returns midway through, it may not use the remaining variables, so the stack size won't increase for those variables.
The compiler may elect to use registers to hold values or place constants directly into the executable code. In this case, they don't add any length to the stack.
Calling Other Functions
The function may call other functions. Each called function may increase the amount of data on the stack. Those functions that are called may call other functions, and so on.
This again, depends on the snapshot in time of the execution. However, one can produce an approximate maximum increase of the stack by the other called functions.
Recursion
As with calling other functions, a recursive call may increase the size of the stack. A recursive call at the end of the function may increase the stack more than a recursive call near the beginning.
Register Value Saving
Sometimes, the compiler may need more space for data than the allocated registers allow. Thus the compiler may push variables on the stack.
The compiler may push registers on the stack for convenience, such as swapping registers or changing the value's order.
Summary
The exact size of stack space required for a function is very difficult to calculate and may depend on where the execution is. There are many items to consider in stack size calculation, such as parameter quantity and size as well as any other functions called. Due to the variability, most stack size measurements are based on a maximum size, or worst case size. Stack allocation is usually based on the worst case scenario.
For an interview question, I would mention all of the above, which usually makes the interviewer want to move on to the next question quickly.
I am trying to better understand items on a stack and how they are addressed. The article I found here seems to indicate that when MIPS stack is initialized, a fixed amount of memory is allocated and the stack grows down to the stack limit which would appear to be smaller addresses. I would assume that based on this logic a stack overflow would occur when 0x0000 was traversed?
I realize MIPS is big endian, but does that change how the stack grows? I wrote what I believed would be a quick way to observe this on an x86_64 machine, but the stack appears to grow up, as I originally assumed it did.
#include <iostream>
#include <vector>
int main() {
std::vector<int*> v;
for( int i = 0; i < 10; i++ ) {
v.push_back(new int);
std::cout << v.back() << std::endl;
}
}
I'm also confused by the fact that not all of the memory address's do not appear to be contiguous, which makes me think I did something stupid. Could somebody please clarify?
The stack on x86 machines also grows downwards. Endianness is unrelated to the direction in which the stack grows.
The stack of a machine has absolutely nothing to do with std::vector<>. Also, new int allocates heap memory, so it tells you absolutely nothing about the stack.
In order to see in which direction the stack grows, you need to do something like this:
recursive( 5 );
void recursive( int n )
{
if( n == 0 )
return;
int a;
printf( "%p\n", &a );
recursive( n - 1 );
}
(Note that if your compiler is smart enough to optimize tail recursion, then you will need to tell it to not optimize it, otherwise the observations will be all wrong.)
essentially there are 3 types of memory you use in programming: static, dynamic/heap and stack.
Static memory is pre-allocated by the compiler and consist of the constants and variables declared statically in your program.
Heap is the memory which you can freely allocate and release
Stack is the memory which gets allocated for all local variables declared in a function. This is important because every time you call the function a new memory for its variables is allocated. So, that every call to a function will assure that it has its own unique copy of the variables. And every time you return from the function the memory gets freed.
It absolutely does not matter how the stack is managed as soon as it follows the above rules. It is convenient however to have program memory to be allocated in the lower address space and grow up, and the stack to start from a top memory space and grow down. Most systems implement this scheme.
In general there is a stack pointer register/variable which points so the current stack address. when a function gets called it decrease this address by the number of bytes it needs for its variables. when it calls the next function, this new one will start with the new pointer already decreased by the caller. When the function returns it restores the pointer which it started from.
There could be different schemes but as far as I know, mips and i86 follow this one.
And essentially there is only one virtual memory space in the program. This is up to the operating system and/or compiler how to use it. The compiler will split the memory in the logical regions for its own use and handle them, hopefully, according to the calling conventions defined in the platform documents.
So, in our example, v and i are allocated on the function stack. cout is static. every new int allocate space in heap. v is not a simple variable but a struct which contains fields which it needs to manage the list. it needs space for all these internals. So, every push_back modifies those fields to point to the allocated 'int' in some way. push_back() and back() are function calls and allocate their own stacks for internal variables to not interfere with the top function.
When I create a new variable in a C++ program, eg a char:
char c = 'a';
how does C++ then have access to this variable in memory? I would imagine that it would need to store the memory location of the variable, but then that would require a pointer variable, and this pointer would again need to be accessed.
See the docs:
When a variable is declared, the memory needed to store its value is
assigned a specific location in memory (its memory address).
Generally, C++ programs do not actively decide the exact memory
addresses where its variables are stored. Fortunately, that task is
left to the environment where the program is run - generally, an
operating system that decides the particular memory locations on
runtime. However, it may be useful for a program to be able to obtain
the address of a variable during runtime in order to access data cells
that are at a certain position relative to it.
You can also refer this article on Variables and Memory
The Stack
The stack is where local variables and function parameters reside. It
is called a stack because it follows the last-in, first-out principle.
As data is added or pushed to the stack, it grows, and when data is
removed or popped it shrinks. In reality, memory addresses are not
physically moved around every time data is pushed or popped from the
stack, instead the stack pointer, which as the name implies points to
the memory address at the top of the stack, moves up and down.
Everything below this address is considered to be on the stack and
usable, whereas everything above it is off the stack, and invalid.
This is all accomplished automatically by the operating system, and as
a result it is sometimes also called automatic memory. On the
extremely rare occasions that one needs to be able to explicitly
invoke this type of memory, the C++ key word auto can be used.
Normally, one declares variables on the stack like this:
void func () {
int i; float x[100];
...
}
Variables that are declared on the stack are only valid within the
scope of their declaration. That means when the function func() listed
above returns, i and x will no longer be accessible or valid.
There is another limitation to variables that are placed on the stack:
the operating system only allocates a certain amount of space to the
stack. As each part of a program that is being executed comes into
scope, the operating system allocates the appropriate amount of memory
that is required to hold all the local variables on the stack. If this
is greater than the amount of memory that the OS has allowed for the
total size of the stack, then the program will crash. While the
maximum size of the stack can sometimes be changed by compile time
parameters, it is usually fairly small, and nowhere near the total
amount of RAM available on a machine.
Assuming this is a local variable, then this variable is allocated on the stack - i.e. in the RAM. The compiler keeps track of the variable offset on the stack. In the basic scenario, in case any computation is then performed with the variable, it is moved to one of the processor's registers and the CPU performs the computation. Afterwards the result is returned back to the RAM. Modern processors keep whole stack frames in the registers and have multiple levels of registers, so it can get quite complex.
Please note the "c" name is no more mentioned in the binary (unless you have debugging symbols). The binary only then works with the memory locations. E.g. it would look like this (simple addition):
a = b + c
take value of memory offset 1 and put it in the register 1
take value of memory offset 2 and put in in the register 2
sum registers 1 and 2 and store the result in register 3
copy the register 3 to memory location 3
The binary doesn't know "a", "b" or "c". The compiler just said "a is in memory 1, b is in memory 2, c is in memory 3". And the CPU just blindly executes the commands the compiler has generated.
C++ itself (or, the compiler) would have access to this variable in terms of the program structure, represented as a data structure. Perhaps you're asking how other parts in the program would have access to it at run time.
The answer is that it varies. It can be stored either in a register, on the stack, on the heap, or in the data/bss sections (global/static variables), depending on its context and the platform it was compiled for: If you needed to pass it around by reference (or pointer) to other functions, then it would likely be stored on the stack. If you only need it in the context of your function, it would probably be handled in a register. If it's a member variable of an object on the heap, then it's on the heap, and you reference it by an offset into the object. If it's a global/static variable, then its address is determined once the program is fully loaded into memory.
C++ eventually compiles down to machine language, and often runs within the context of an operating system, so you might want to brush up a bit on Assembly basics, or even some OS principles, to better understand what's going on under the hood.
Lets say our program starts with a stack address of 4000000
When, you call a function, depending how much stack you use, it will "allocate it" like this
Let's say we have 2 ints (8bytes)
int function()
{
int a = 0;
int b = 0;
}
then whats gonna happen in assembly is
MOV EBP,ESP //Here we store the original value of the stack address (4000000) in EBP, and we restore it at the end of the function back to 4000000
SUB ESP, 8 //here we "allocate" 8 bytes in the stack, which basically just decreases the ESP addr by 8
so our ESP address was changed from
4000000
to
3999992
that's how the program knows knows the stack addresss for the first int is "3999992" and the second int is from 3999996 to 4000000
Even tho this pretty much has nothing to do with the compiler, it's really important to know because when you know how stack is "allocated", you realize how cheap it is to do things like
char my_array[20000];
since all it's doing is just doing sub esp, 20000 which is a single assembly instruction
but if u actually use all those bytes like memset(my_array,20000) that's a different history.
how does C++ then have access to this variable in memory?
It doesn't!
Your computer does, and it is instructed on how to do that by loading the location of the variable in memory into a register. This is all handled by assembly language. I shan't go into the details here of how such languages work (you can look it up!) but this is rather the purpose of a C++ compiler: to turn an abstract, high-level set of "instructions" into actual technical instructions that a computer can understand and execute. You could sort of say that assembly programs contain a lot of pointers, though most of them are literals rather than "variables".
As mentioned in this tutorial:
http://www.learncpp.com/cpp-tutorial/79-the-stack-and-the-heap/
In computer programming, a stack is a container that holds other
variables (much like an array). However, whereas an array lets you
access and modify elements in any order you wish, a stack is more
limited. The operations that can be performed on a stack are identical
to the ones above:
1) Look at the top item on the stack (usually done via a function
called top()) 2) Take the top item off of the stack (done via a
function called pop()) 3) Put a new item on top of the stack (done via
a function called push())
But if I defined two variables in C++ I do not have to use them in the same order of definition:
Example:
int main() {
int a;
int b;
b = 5;
a = 6;
}
Is there a problem on this code? I can use them in any order I like!! I do not have to use the a first then the b.
Am I misunderstanding something? What is it?
You are confusing two different kinds of stacks.
One stack, is where some of the memory for your application is allocated. This would be part of a discussion about the stack and heap and where your memory is allocated.
The other kind of stack is a data structure that conforms to a LIFO style of access. This could be implemented using a std::vector or other forms of data structures.
Yep, the "automatic storage" that holds a called method's local variables is allocated in a stack. But with the automatic storage stack you push and pop (variable sized) "stack frames" that contain all of the local variables for a method, not individual values.
This is conceptually similar to the kind of stack discussed in the article you quote, except that the items being pushed and popped are much larger.
In both cases the mechanism is "LIFO", since when you call a method you essentially return by "popping the stack" -- you always have to return from methods in the opposite order that you called them.
Am I misunderstanding something? What is it?
The "stack" that you're putting 'a' and 'b' on is (WARNING HUGE SIMPLIFICATIONS AND CONCEPTUALIZATIONS AHEAD) not made out of variables; it is made out of stack frames. A stack frame consists of the parameters to a function and a space for its return value, and sometimes also the variables used within the function (except that these can also be held in registers, and sometimes parameters are also passed via the registers). "Pushing" onto this stack is accomplished by calling a function. "Popping" from this stack is accomplished by returning from a function. You indeed only have access to the "top" element; you can't just read the variables of the function that called the current function, unless they were explicitly passed in as parameters.
Like any other data structure , stack is a data structure which follows LIFO(last in first out) principle.As mentioned in your question it does push and pop operation for inputting and retrieving data according to LIFO principle.
Every process consists of basically 4 portions of address space that are
accessible to the process
when it is running
Text - This portion contains the actual m/c instructions to be
executed. On many Operating Systems this is set to read only, so that the
process can't modify its instructions. This allows multiple instances of
the program to share the single copy of the text.
Data - This portion contains the program's data part. It furthere
divided into
1) Initialized Read Only Data - This contains the data elements
that are initialized by the program and they are read only during the
execution of the process.
2) Initialized Read Write Data - This contains the data elements
that are initialized by the program and will be modified in the course of
process execution.
3)Uninitalized Data - This contains the elements are not
initialized by the program and are set 0 before the processes executes.
These can also be modified and referred as BSS(Block Started Symbol). The
adv of such elements are, system doesn't have to allocate space in the
program file for this area, b'coz it is initialized to 0 by OS before the
process begins to execute.
Stack - This portion is used for local variables, stack frames
Heap - This portion contains the dynamically allocated memory
int abc = 1; ----> Initialized Read-Write Data
char *str; ----> BSS
const int i = 10; -----> Initialized Read-Only Data
main()
{
int ii,a=1,b=2,c; -----> Local Variables on
Stack
char *ptr;
ptr = malloc(4); ------> Allocated Memory in Heap
c= a+b; ------> Text
}
Data, store data
Text, store code
There are 3 (main?) segments/sections of the file produced by a linker.
text - program text (and apparently const char arrays. maybe other 'const' arrays, since those can not be changed anyway). I am not 100% sure about the array part, maybe
someone will correct me.
data - initialized global data. see examples below.
bss - uninitialized global data.
Here are some examples
int x = 1; /* goes into data */
int y; /* goes into bss */
const int z = 1;
this, we've seen go into 'text', since can't be changed anyway,but can be protected
const char array[] = {'a','b'....etc}
/* the rest goes into text */
int main(void)
{
return EXIT_SUCCESS;
}
Block Started by Symbol
(BSS) The uninitialised data segment produced by Unix linkers. The other segments are the "text" segment which contains the program code and the "data" segment contains
initialised data. Objects in the bss segment have only a name and a size but no value.
You don't have to use them in the order defined. But they are destroyed - taken off the stack- in that order. LIFO does not refer to access, only putting things on or taking them off the stack.
You can trivially observe this by changing int for a type which prints in it's destructor.
A stack is a standard data structure that's used all over the place. A thread's so called stack is in fact an implementation of this paradigm. There's a stack pointer (usually in a processor register) that points to a memory location. Data is 'pushed' onto this stack by moving the sp. We 'pop' by returning the value it points to and moving the sp the opposite direction.
As far as your a, b declared above it makes no difference. They're both allocated before they get used.
Stack program:
Your example program is not an implementation of stack. Stack shall store elements like an array (or by some means) where elements can be pushed (stored) or poped (pulled out) in LIFO (last in first out) order. It's implementation is not as simple as declaring two variables. Standard C++ provides stack class so that you can use it without having to implement on your own.
Stack Memory:
Variables in a function are stored in stack memory (Somewhere in the RAM) in LIFO order. From your example, Variable a will be created and pushed to stack memory. Then, the variable b is pushed to stack memory. After the function completes it's execution, the variables are destroyed in LIFO fashion. So variable b is the first to get destroyed and finally variable a gets destroyed. You don't write the code for it. The compiler takes care to write assembly low level code for it.