C pointer array scope and function calls - c++

I have this situation:
{
float foo[10];
for (int i = 0; i < 10; i++) {
foo[i] = 1.0f;
}
object.function1(foo); // stores the float pointer to a const void* member of object
}
object.function2(); // uses the stored void pointer
Are the contents of the float pointer unknown in the second function call? It seems that I get weird results when I run my program. But if I declare the float foo[10] to be const and initialize it in the declaration, I get correct results. Why is this happening?

For the first question, yes using foo once it goes out of scope is incorrect. I'm not sure if it's defined behavior in the spec or not but it's definitely incorrect to do so. Best case scenario is that your program will immediately crash.
As for the second question, why does making it const work? This is an artifact of implementation. Likely what's happenning is the data is being written out to the data section of the DLL and hence is valid for the life of the program. The original sample instead puts the data on the stack where it has a much shorter lifetime. The code is still wrong, it just happens to work.

Yes, foo[] is out of scope when you call function2. It is an automatic variable, stored on the stack. When the code exits the block it was defined in, it is deallocated. You may have stored a reference (pointer) to it elsewhere, but that is meaningless.

In both cases you are getting undefined behaviour. Anything might happen.
You are storing a pointer to the locally declared array, but once the scope containing the array definition is exited the array - and all its members are destroyed.
The pointer that you have stored now no longer points to a float or even a valid memory address that could be used for a float. It might be an address that is reused for something else or it might continue to contain the original data unchanged. Either way, it is still not valid to attempt to dereference the pointer, either for reading or writing a float value.

For any declaration like this:
{
type_1 variable_name_1;
type_2 variable_name_2;
type_3 variable_name_3;
}
declaration, the variables are allocated on the stack.
You can print out the address of each variable:
printf("%p\n", variable_name )
and you'll see that addresses increase by small amount roughly (but not always exactly equal to), the amount of space each variable needs to store its data.
The memory used by stack variables is recycled when the '}' is reached and the variables go out of scope. This is done nice an efficiently just by subtracting some number from a special pointer called the 'stack pointer', which says where the data for new stack variables will have their data allocated. By incrementing and decrementing the stack pointer, programs have an extremely fast way of working out were the memory for variables will live. Its such and important concept that every major processor maintains a special piece of memory just for the stack pointer.
The memory for your array is also pushed and popped from the program's data stack and your array pointer is a pointer into the program's stack memory. While the language specification says accessing the data owned by out-of-scope variables has undefined consequences, the result is typically easy to predict. Usually, your array pointer will continue to hold its original data until new stack variables are allocated and assigned data (i.e. the memory is reused for other purposes).
So don't do it. Copy the array.
I'm less clear about what the standard says about constant arrays (probably the same thing -- the memory is invalid when the original declaration goes out of scope). However, your different behavior is explainable if your compiler allocated a chunk of memory for constants that is initialized when your program starts, and later, foo is made to point to that data when it comes into scope. At least, if I were writing a compiler, that's probably what I'd do as its both very fast and leads to using the smallest amount of memory. This theory is easily testable in the following way:
void f()
{
const float foo[2] = {99, 101};
fprintf( "-- %f\n", foo[0] );
const_cast<foo*>(foo)[0] = 666;
}
Call foo() twice. If the printed value changed between calls (or an invalid memory access exception is thrown), its a fair bet that the data for foo is allocated in special area for constants that the above code wrote over.
Allocating the memory in a special area doesn't work for non-const data because recursive functions may cause many separate copies of a variable to exist on the stack at the same time, each of which may hold different data.

It's undefined behavior in both cases. You should consider the stack based variable deallocated when control leaves the block.

What's happening is currently you're probably just setting a pointer (can't see the code, so I can't be sure). This pointer will point to the object foo, which is in scope at that point. But when it goes out of scope, all hell can break loose, and the C standard can make no guarantees about what happens to that data once it goes out of scope. It can be overwritten by anything. It works for a const array because you're lucky. Don't do that.
If you want the code to work correctly as it is, function1() is going to need to copy the data into the object member. Which means you'll also have to know the length of the array, which means you'll have to pass it in or have some nice termination method.

The memory associated with foo goes out of scope and is reclaimed.
Outside the {}, the pointer is invalid.
It is a good idea to make objects manage their own memory rather than refer to an external pointer. In this specific case your object could allocate its own foo internally and copy the data into it. However it really depends on what you are trying to achieve.

For simple problems like this it is better to give a simple answer, not 3 paragraphs about stacks and memory addresses.
There are 2 pairs of braces {}, one is inside the other. The array was declared after the first left brace { so it stops existing before the last brace }
The end
When answering a question you must answer it at the level of the person asking regardless of how well you yourself comprehend the issue or you may confuse the student.
-experienced ESL teacher

Related

C++. interview. about scope that return int* value issue

JUST TO CLARIFY, THIS IS AN AMAZON INTERVIEW QUESTION. SO BE CONSTRUCTIVE!
This is from a website I am reviewing for interviewing.
The question is to append 2 arrays in the form of the function.
// a1(size1) and a2(size2) are arrays and you
// have to append them.
int* Append(int* a1,int* a2,int size1,int size2)
The question that I have is. From the code below, why The array a is actually in the function Append and once you leave the function the scope of this array ends?
#include <iostream>
#include <string>
using namespace std;
int* Append(int* a1,int* a2,int size1,int size2)
{
int a[size1+size2],i;
for(i=0;i<size1;i++)
{
a[i]=a1[i];
}
for(i=0;i<size2;i++)
{
a[i+size1]=a2[i];
}
return a;
}
int main()
{
int a[] = {1,2,3};
int b[] = {4,5,6,7};
int* c;
c = Append(a,b,3,4);
for(i=0;i<7;i++)
cout << c[i] << endl;
cout << "abc";
}
"I told him that the above code actually fails. He asked me why it fails. I answered him this way.The array a is actually in the function Append and once you leave the function the scope of this array ends. He asked me how to do it then. I had no idea. Later he told me that we have to allocate memory for the array using malloc.Later on he explained me how it works using heap memory."
The question that I have is. From the code above, why The array a is actually in the function Append and once you leave the function the scope of this array ends?
My second question is that is this related to Heap and Stack memory?
How do the Heap and stack memory work in Java? And how is the Heap and Stack in Java different from C++? (I program more in Java, so I would like to learn more about the memory in Java)
Please attach any link/url about the material, I would like to learn more.
C++ support the basic data structures that C does, such as simple arrays, but that does not mean it is encouraged to do so. In C++ you are generally better of not writing basic arrays, but instead using the std::vector class, or similar data structure.
That being said, the question was an interview question, so even though this approach is discouraged, in general, let us look at the function, and the initialization and allocation of the array.
The first thing to notice is that the code does not compile. The main function holds a loop with a loop variable i that is not declared. This is a minor thing, and we can quickly fix it. Compiler output:
prog.cpp: In function ‘int main()’:
prog.cpp:29:7: error: ‘i’ was not declared in this scope
for(i=0;i<7;i++)
^
Once we have fixed the compiler error we can run the code, but the compiler will still provide some warnings, which I strongly suggest you look at:
prog.cpp: In function ‘int* Append(int*, int*, int, int)’:
prog.cpp:8:9: warning: address of local variable ‘a’ returned [-Wreturn- local-addr]
int a[size1+size2],i;
^
The warning has all the information we need at this point. (I do know that at an interview you might not have access to a compiler to see these things, but they are helpful when you get home and wonder why you got the reply like you did).
The warning basically says that the funtion is returning the address of a local variable. The local variable is deallocated once it leaves scope, that is how the stack works. We push to the stack when we declare the variables, and once the scope the variable is declared in is left, we pop the variables from the scope (or rather the allocated memory from the variables).
So the local variable a, is declared in the beginning of the function on the stack, and once the function returns, the address of this local variable is returned, but the memory the variable reserved is released.
So yes, this is related to heap and stack, in that elements on the stack are alive while the scope they are declared in, is alive, while elements on the heap are kept alive until they are explicitly told to no longer be alive (using delete or free).
When you are told that you need to allocate the array on the heap, you can do so using the new[] operator:
int* a = new a[size1 + size2];
Remember that something that is allocated using new[] must be deallocated using delete[]. I do not agree with using malloc but this might be some requirement for the code, that will at some point release the code with free, at which point you have no choice but using malloc.
Again I would personally not do either, I would use std::vector instead, why worry about this, when it could be as simple as: std::vector<int> a? As an interview question though, it was asked to test your core language understanding, and while it would be good to be able to answer how to allocate the array on the heap, it would show a bit more strength to be able to reflect upon the code, and recommend an alternative, and better, approach. Such as not using new/malloc.
Generally C++ returns values and parses parameters by value (when we are not using the by reference option). Which means that we return a copy of the value we return. This is how we are able to return simple integers, or doubles, or even std::string (we ignore the move constructors, and how they work for simplicity), because we make a copy of them, and return. Actually we also make a copy of the variable a and return that. So you may wonder, why it fails when we make a copy of a. But as we have already established, from the compiler warning, the value we are actually returning, is the address of a.
When we declare an array in C++ (or C), we are allocating an area in memory, that is large enough to hold the requested number of elements. The variable, will then be a pointer to the address of the first element. It is that address we are actually copying, and not the actual area that it points to. So the function will return a pointer to an area, that is no longer allocated. The local variable a, is no longer alive, but the contents of a is copied, and is returned by the function.
Java is higher level language than C++ and does allow you to manipulate memory in the same way. In Java everything that is not a primitive (int, double, ...) is kept on the heap. Java objects are freed when there are no longer any references to them.
1: Yes that is correct. This would also often trigger a warning by the compiler that you are "Returning address of temporary" or something similar.
2: Yes. All variables you declare in the scope of the function will be placed on the stack and will be automatically destroyed when the scope(the function) ends. To make it persist until you tell it not to(using delete/free) you have to allocate the memory using new or malloc.
3: I've only written very little Java so I can't answer this fully but when programming in Java you don't have to think about stack vs heap memory(although it might be beneficial to know how it works). Java has a "garbage collector" running in the background that cleans up any heap memory that it allocates for you (all variables except primitives as int, float etc are allocated on the heap in Java) when it's not needed anymore. This does of course come at a performance cost though.
Over here you can also see a brief summary of the different ways to handle memory in C++ :)
Proper stack and heap usage in C++?
Edit 1: I just noticed now by the way that this program doesn't even compile.
In the main function; there is a for loop that does i = 0 without first declaring i as an integer(or whatever type they intended it to be).
Edit 2: Also; as another commenter pointed out; int a[size1+size2] is not legal. For it to be legal; size1 and size2 would have to be const non parameter variables or a would have to be allocated on the heap.

Why doesn't c++ compiler remove the variable that is out of lifetime immediately?

From Scope vs. Lifetime of Variable we see the part of anser from Alok:
Note that technically x does not exist beyond its scope however it
might happen that the compiler did not remove the contents of x and
one might be able to access contents of x beyond its scope through a
pointer(as you do).However, a code which does this is not a valid C++
code. It is a code which invokes Undefined Behaviour. Which means
anything can happen(you might even see value of x being intact) and
one should not expect observable behaviors from such a code.
That is interesting. Why not remove the variable x once it reach }?
Same concept as when you delete a file on your hard disk, why does it just delete the index entry rather than format the file?
It takes time.
There's no point in wiping memory if there's no need to do so. The compiler will use that chunk of memory again later when you make another local variable, but until it does so it will just ignore the contents.
Why not remove the variable x once it reach }?
What do you mean with "remove the variable x?"
The only thing that comes close to "removing" a variable is overwriting it's value at &x. There's no reason for a compiler to do this because this would waste time for nothing. Instead, when a variable reaches the end of it's scope the code can just no longer access x through it's name (and thus it's memory address is no longer valid to the programmer) and the memory address becomes available to the system again. Then, when at one point some other variable is created and needs that address space the value of that variable is just written to &x, overwriting it's previous value.
This is also why it's undefined behaviour to read an unitinitalized variable, a variable declaration like int x; only reserves some space, the address still holds the value that was there earlier and needs to be overwritten by the programmer explicitly ( int x = 0;) .
When a variable passes out of scope, the compiler is allowed to assume no code will try to access it - since any attempt to do so gives undefined behaviour.
Since the compiler is allowed to assume no code accesses a non-existent variable, it can do what it likes with the memory occupied by that variable after it ceases to exist. It can choose to reclaim the memory. It can simply leave the memory alone, and reallocate it for some other use when it needs to. It can encrypt the contents of the memory if it likes.
Practically, the reason a compiler will not reclaim the memory is performance - there is a performance hit in reclaiming memory, cleaning up that memory to allow it to be reused by other code, etc. And, since the compiler is not required to do any of those things, few compiler vendors will deliberately do something that gives an unnecessary performance hit.

Returning an object: value, pointer and reference

I know this has probably been asked and I've looked through other answers, but still I cannot get this completely.
I want to understand the difference between the two following codes:
MyClass getClass(){
return MyClass();
}
and
MyClass* returnClass(){
return new MyClass();
}
Now let's say I call such functions in a main:
MyClass what = getClass();
MyClass* who = returnClass();
If I got this straight, in the first case the object created in the
function scope will have automatic storage, i.e. when you exit the
scope of the function its memory block will be freed. Also, before
freeing such memory, the returned object will be copied into the
"what" variable I created. So there will exist only one copy of
the object. Am I correct?
1a. If I'm correct, why is RVO (Return Value Optimization) needed?
In the second case, the object will be allocated through a dynamic storage, i.e. it will exist even out of the function scope. So I need to use a deleteon it. The function returns a pointer to such object, so there's no copy made this time, and performing delete who will free the previously allocated memory. Am I (hopefully) correct?
Also I understand I can do something like this:
MyClass& getClass(){
return MyClass();
}
and then in main:
MyClass who = getClass();
In this way I'm just telling that "who" is the same object as the one created in the function. Though, now we're out of the function scope and thus that object doesn't necessarily exists anymore. So I think this should be avoided in order to avoid trouble, right? (and the same goes for
MyClass* who = &getClass();
which would create a pointer to the local variable).
Bonus question: I assume that anything said till now is also true when returning vector<T>(say, for example, vector<double>), though I miss some pieces.
I know that a vector is allocated in the stack while the things it contains are in the heap, but using vector<T>::clear() is enough to clear such memory.
Now I want to follow the first procedure (i.e. return a vector by value): when the vector will be copied, also the onjects it contains will be copied; but exiting the function scope destroys the first object. Now I have the original objects that are contained nowhere, since their vector has been destroyed and I have no way of deleting such objects that are still in the heap. Or maybe a clear() is performed automatically?
I know that I may beatray some misunderstandings in these subjects (expecially in the vector part), so I hope you can help me clarify them.
Q1. What happens conceptually is the following: you create an object of type MyClass on the stack in the stack frame of getClass.
You then copy that object into the return value of the function, which is a bit of stack that was allocated before the function call to hold this object.
Then the function returns, the temporary gets cleaned up. You copy the return value into the local variable what. So you have one allocation and two copies.
Most (all?) compilers are smart enough to omit the first copy: the temporary is not used except as return value. However, the copy from the return value into the local variable on the caller side cannot be omitted, because the return value lives on a part of the stack that is freed as soon as the function finishes.
Q1a. Return Value Optimization (RVO) is a special feature, that does allow that final copy to be elided. That is, instead of returning the function result on the stack, it will be allocated straight away in the memory allocated for what, avoiding all copying altogether. Note that, contrary to all other compiler optimizations, RVO can change the behaviour of your program! You could give MyClass a non-default copy constructor, that has side effects, like printing a message to the console or liking a post on Facebook. Normally, the compiler is not allowed to remove such function calls unless it can prove that these side effects are absent. However, the C++ specs contain a special exception for RVO, that says that even if the copy constructor does something non-trivial, it is still allowed to omit the return value copy and reduce the whole thing to a single constructor call.
2. In the second case, the MyClass instance is not allocated on the stack, but on the heap. The result of the new operator is an integer: the address of the object on the heap. This is the only point where you will ever be able to obtain this address (provided you didn't use placement new), so you need to hold onto it: if you lose it, you cannot call delete and you will have created a memory leak.
You assign the result of new to a variable whose type is denoted by MyClass* so that the compiler can do type checking and stuff, but in memory it is just an integer large enough to hold an address on your system (32- or 64-bits). You can check this for yourself by trying to coerce the result to a size_t (which is typedef'd to typically an unsigned int or something larger depending on your architecture) and seeing the conversion succeed.
This integer is returned to the caller by value, i.e. on the stack, just as in example (1). So again,
in principle, there is copying going on, but in this case only copying of a single integer which your CPU is very good at (most of the times it will not even go on the stack but get passed in a register) and not the whole MyClass object (which in general has to go on the stack because it's very large, read: larger than an integer).
3. Yes, you should not do that. Your analysis is correct: as the function finishes, the local object is cleaned up and its address becomes meaningless. The problem is, that it sometimes seems to work. Forgetting about optimizations for the time being, the main reason the way memory works: clearing (zero-ing) memory is quite expensive, so that is hardly ever done. Instead, it is just marked as available again, but it's not overwritten until you make another allocation that needs it. Therefore, even though the object is technically dead, its data may still be in the memory so when you dereference the pointer you may still get the right data back. However, since the memory is technically free, it may be overwritten at any time between right now and at the end of the universe. You have created what C++ calls Undefined Behaviour (UB): it may seem to work right now on your computer, but there's no telling what may happen somewhere else or at another point in time.
Bonus: When you return a vector by value, as you remarked, it is not just destroyed: it is first copied to the return value or - taking RVO into account - into the target variable. There are two options now: (1) The copy creates its own objects on the heap, and modifies its internal pointers accordingly. You now have two proper (deep) copies co-existing temporarily -- then when the temporary object goes out of scope, you are just left with the one valid vector. Or (2): When copying the vector, the new copy takes ownership of all the pointers that the old one holds. This is possible, if you know that the old vector is about to be destroyed: rather than re-allocating all the contents again on the heap, you can just move them to the new vector and leave the old one in a sort of half-dead state -- as soon as the function is done cleaning that stack the old vector is no longer there anyway.
Which of these two options is used, is really irrelevant or rather, an implementation detail: they have the same result and whether the compiler is smart enough to choose (2) should not usually be your concern (though in practice option (2) will always happen: deep copying an object just to destroy the original is just pointless and easily avoided).
As long as you realize that the thing that gets copied is the part on the stack and the ownership of the pointers on the heap gets transferred: no copying happens on the heap and nothing gets cleared.
Here are my answers to your different questions:
1- You are absolutely correct. If I understand the sequentiallity correctly, your code will allocate memory, create your object, copy the variable into the what variable, and get destroyed as out of scope. The same thing happens when you do:
int SomeFunction()
{
return 10;
}
This will create a temporary that holds 10 (so allocate), copy it to the return vairbale, and then destroy the temporary (so deallocate) (Here I'm not sure of the specifics, maybe the compiler can remove some stuff via automatic inlining, constante values, ... but you get the idea). Which brings me to
1a- You need RVO when to limit this allocation, copy, and deallocation part. If your class allocates a lot of data upon construction it is a bad idea to return it directly. You can use move constructor in that case, and reuse the storage space allocated by the temporary for example. Or return a pointer. Which takes all the way down to
2- Returning a pointer works exactly as returning an int from a function. But because pointers are only 4 or 8 bytes long, allocation and deallocation cost a lot less than doing so for a class that's 10 Mb long. And instead of copying the object you copy its adress on the heap (usually less heavy, but copy nonetheless). Do not forget it is not because a pointer represents a memory that its size is 0 byte. So using a pointer requires getting the value from some memory address. Returning a reference and inlining are also good ideas to optimise your code, as you avoid chasing pointer, function calls, etc.
3- I think you are correct there. I'd have to make sure by testing, but if follow my logic you are right.
I hope I answered your questions. And I hope my answers are as correct as can be. But maybe someone more clever than me can correct me :-)
Best.

Why can't you free variables on the stack?

The languages in question are C/C++.
My prof said to free memory on the heap when your done using it, because otherwise you can end up with memory that can't be accessed. The problem with this is you might end up with all your memory used up and you are unable to access any of it.
Why doesn't the same concept apply to the stack? I understand that you can always access the memory you used on the stack, but if you keep creating new variables, you will eventually run out of space right? So why can't you free variables on the stack to make room for new variables like you can on the heap?
I get that the compiler frees variables on the stack, but thats at the end of the scope of the variable right. Doesn't it also free a variable on the heap at the end of its scope? If not, why not?
Dynamically allocated objects ("heap objects" in colloquial language) are never variables. Thus, they can never go out of scope. They don't live inside any scope. The only way you can handle them is via a pointer that you get at the time of allocation.
(The pointer is usually assigned to a variable, but that doesn't help.)
To repeat: Variables have scope; objects don't. But many objects are variables.
And to answer the question: You can only free objects, not variables.
The end of the closed "}" braces is where the stack "frees" its memory. So if I have:
{
int a = 1;
int b = 2;
{
int c = 3; // c gets "freed" at this "}" - the stack shrinks
// and c is no longer on the stack.
}
} // a and b are "freed" from the stack at this last "}".
You can think of c as being "higher up" on the stack than "a" and "b", so c is getting popped off before them. Thus, every time you write a "}" symbol, you are effectively shrinking the stack and "freeing" data.
There are already nice answers but I think you might need some more clarification, so I'll try to make this a more detailed answer and also try to make it simple (if I manage to). If something isn't clear (which with me being not a native english speaker and having problems with formulating answers sometimes might be likely) just ask in the comments. Also gonna take the use the Variables vs Objects idea that Kerrek SB uses in his answer.
To make that clearer I consider Variables to be named Objects with an Object being something to store data within your program.
Variables on the stack got automatic storage duration they automatically get destroyed and reclaimed once their scope ends.
{
std::string first_words = "Hello World!";
// do some stuff here...
} // first_words goes out of scope and the memory gets reclaimed.
In this case first_words is a Variable (since it got its own name) which means it is also an Object.
Now what about the heap? Lets describe what you might consider being "something on the heap" as a Variable pointing to some memory location on the heap where an Object is located. Now these things got what's called dynamic storage duration.
{
std::string * dirty = nullptr
{
std::string * ohh = new std::string{"I don't like this"} // ohh is a std::string* and a Variable
// The actual std::string is only an unnamed
// Object on the heap.
// do something here
dirty = ohh; // dirty points to the same memory location as ohh does now.
} // ohh goes out of scope and gets destroyed since it is a Variable.
// The actual std::string Object on the heap doesn't get destroyed
std::cout << *dirty << std::endl; // Will work since the std::string on the heap that dirty points to
// is still there.
delete dirty; // now the object being pointed to gets destroyed and the memory reclaimed
dirty = nullptr; can still access dirty since it's still in its scope.
} // dirty goes out of scope and get destroyed.
As you can see objects don't adhere to scopes and you got to manually manage their memory. That's also a reason why "most" people prefer to use "wrappers" around it. See for example std::string which is a wrapper around a dynamic "String".
Now to clarify some of your questions:
Why can't we destroy objects on the stack?
Easy answer: Why would you want to?
Detailed answer: It would be destroued by you and then destroyed again once it leaves the scope which isn't allowed. Also you should generally only have variables in your scope that you actually need for your computation and how would you destroy it if you actually need that variable to finish your computation? But if you really were to only need a variable for a small time within a computation you could just make a new smaller scope with { } so your variable gets automatically destroyed once it isn't needed anymore.
Note: If you got a lot of variables that you only need for a small part of your computation it might be a hint that that part of the computation should be in its own function/scope.
From your comments: Yeah I get that, but thats at the end of the scope of the variable right. Doesn't it also free a variable on the heap at the end of its scope?
They don't. Objects on the heap got no scope, you can pass their address out of a function and it still persists. The pointer pointing to it can go out of scope and be destroyed but the Object on the heap still exists and you can't access it anymore (memory leak). That's also why it's called manual memory management and most people prefer wrappers around them so that it gets automatically destroyed when it isn't needed anymore. See std::string, std::vector as examples.
From your comments: Also how can you run out of memory on a computer? An int takes up like 4 bytes, most computers have billions of bytes of memory... (excluding embedded systems)?
Well, computer programs don't always just hold a few ints. Let me just answer with a little "fake" quote:
640K [of computer memory] ought to be enough for anybody.
But that isn't enough like we all should know. And how many memory is enough? I don't know but certainly not what we got now. There are many algorithms, problems and other stuff that need tons of memory. Just think about something like computer games. Could we make "bigger" games if we had more memory? Just think about it... You can always make something bigger with more resources so I don't think there's any limit where we can say it's enough.
So why can't you free variables on the stack to make room for new variables like you can on the heap?
All information that "stack allocator" knows is ESP which is pointer to the bottom of stack.
N: used
N-1: used
N-2: used
N-3: used <- **ESP**
N-4: free
N-5: free
N-6: free
...
That makes "stack allocation" very efficient - just decrease ESP by the size of allocation, plus it is locality/cache-friendly.
If you would allow arbitrary deallocations, of different sizes - that will turn your "stack" into "heap", with all associated additional overhead - ESP would not be enough, because you have to remember which space is deallocated and which is not:
N: used
N-1: free
N-2: free
N-3: used
N-4: free
N-5: used
N-6: free
...
Clearly - ESP is not more enough. And you also have to deal with fragmentation problems.
I get that the compiler frees variables on the stack, but thats at the end of the scope of the variable right. Doesn't it also free a variable on the heap at the end of its scope? If not, why not?
One of the reasons is that you don't always want that - sometimes you want to return allocated data to caller of your function, that data should outlive scope where it was created.
That said, if you really need scope-based lifetime management for "heap" allocated data (and most of time it is scope-based, indeed) - it is common practice in C++ to use wrappers around such data. One of examples is std::vector:
{
std::vector<int> x(1024); // internally allocates array of 1024 ints on heap
// use x
// ...
} // at the end of the scope destructor of x is called automatically,
// which does deallocation
Read about function calls - each call pushes data and function address on the stack. Function pops data from stack and eventually pushes its result.
In general, stack is managed by OS, and yes - it can be depleted. Just try doing something like this:
int main(int argc, char **argv)
{
int table[1000000000];
return 0;
}
That should end quickly enough.
Local variables on the stack don't actually get freed. The registers pointing at the current stack are just moved back up and the stack "forgets" about them. And yes, you can occupy so much stack space that it overflows and the program crashes.
Variables on the heap do get freed automatically - by the operating system, when the program exits. If you do
int x;
for(x=0; x<=99999999; x++) {
int* a = malloc(sizeof(int));
}
the value of a keeps getting overwritten and the place in the heap where a was stored is lost. This memory is NOT freed, because the program doesn't exit. This is called a "memory leak". Eventually, you will use up all the memory on the heap, and the program will crash.
The heap is managed by code: Deleting a heap allocation is done by calling the heap manager. The stack is managed by hardware. There is no manager to call.

Arduino array memory usage

If I declare an array in the global scope, it uses up memory to store it. However, if I declare an array (I am using two types, one is a char array, while the other is an int array) inside a function (such as setup()) will the memory be freed automatically once the array goes out of scope?
I believe this happens for some variables such as int or byte. I just wanted to know if this applies to arrays as well.
Also, since I read that for programs containing lots of strings, it is best to store them in program space, does a call such as
lcd.print("Hello")
still use up the memory for the "Hello" string after the function ends (assuming that the print function does not store it someplace else)?
To the second question:
The F() macro will store strings in the progmen instead of using RAM, so you do not have this problem anymore:
lcd.print(F("Hello"));
As to your 1st question:
Yes. All variables declared inside a function are only valid inside until the function returns and are released automatically then. This has some implications:
You must not use a pointer to a locally declared variable after the variable went out of scope, for instance, after the function returned. (Don't return a pointer to a local array from your function!) - It is however perfecly legal to pass that pointer to other functions when calling them from within the declaring block/function.
Local variables are stored on the local stack so that there needs to be enough room left for the stack to grow by the corresponding number of bytes when the function is called.
The amount of memory used by those variables is not accounted for in the calculation of "used" RAM at compile time.