The following answer is based on my experience looking at mapfiles, if I'm wrong about sth. please correct me!
Static vars are definitely not stored on the heap, since this is only for variables allocated during run time (and static vars are allocated during compile time).
Static variables are stored in RAM, just like your global variables. The scope of a certain variable matters only to the compiler, at the machine code level nobody prevents you from reading a local variable outside of a function (as long as your controller doesn't have some fancy features to protect memory areas from being accessed).
And global variables are stored in Flash.
No. Think about it: To write a single bit of flash, you have to erase a whole block of data an then rewrite the whole thing with the modified data. And these steps do not execute in a single cycle like a simple store into RAM does. Global variables are stored in RAM, just like mentioned before. This solves also your confusion about flash-less systems.
In general the stack and the heap crash in to each other. At that point it all gets messy.
Depending on the MCU one of several things may (or will) happen.
- Variables get corrupted
- The stack gets corrupted
- The program gets corrupted
When 1 happens you start getting strange behaviour - things not doing what they should.
When 2 happens all manner of hell breaks loose. If the return address on the stack (if there is one) is corrupted, then where the current call will return to is anyone's guess. At that time basically the MCU will start doing random things.
When 3 happens again, who knows quite what would happen. This only happens when you're executing code out of RAM.
In general when the stack gets corrupted it's all over. Just what happens is down to the MCU.
It might be that trying to allocate the memory in the first place fails so the corruption doesn't happen. In this case the MCU might raise an exception. If there is no exception handler installed, then most often the MCU will just halt (an equivalent of while (1);
. If there is a handler installed, then it might reboot cleanly.
If the memory allocation does go ahead, or if it tries, fails, and just continues with no memory allocated, then you're into the realms of "who knows?". The MCU might end up rebooting itself through the right combination of events (interrupts caused that end up resetting the chip, etc), but there's no guarantee of that happening.
What there can usually be a high probability of happening, though, if it's enabled, is the internal watchdog timer (if one exists) timing out and rebooting the chip. When the program goes completely AWOL through this kind of crash the instructions to reset the timer generally won't be run, so it will time out and reset.
Best Answer
Everything is done in "sections". Where a section goes depends on its use. What gets put into a section, again, depends on its use.
In general you get two "data" sections - .data and .rodata. .data is stored in RAM and is read-write. .rodata is stored in Flash and is read-only.
Things that are "const" generally end up in .rodata. Literals (string, numeric, etc) also end up in .rodata.
For instance, these would normally be stored in Flash:
These will be stored in RAM:
But then of course, it can be more MCU specific, especially when you get into Harvard (split RAM / Flash address spaces) architecture, where accessing data stored in Flash can be more of a special task and not done as normal variables. For instance, the
const char *foo="foo";
would be copied into RAM for easy access during the initialization of the program.When you allocate a variable of any size within a function, such as:
the variable will be allocated as part of the stack (or an emulated stack-like system using static variables on MCUs with a limited hardware stack, such as low-end 8-bit PICs). As soon as you return from the function all the stack space is reclaimed.
However, if you allocate it using malloc():
it will be allocated on the heap, and it is your responsibility to reclaim that space using free().
The
register
keyword, as in:will, if a register can be allocated, store the data purely within that allocated register. It will never touch RAM, and consequently it will run faster. However, if a register cannot be allocated (they're all in use in the code) then the register keyword will be ignored and the variable allocated in RAM as per normal. One thing to note:
That will only hold the pointer to the data in a register, not the data itself. The data itself will still be in RAM.
In general, though, the use of the
register
keyword is frowned upon since the compiler's optimizer usually does a better job, and usingregister
can in fact cause other parts of your code to not be optimized as well and end up more complex than they would otherwise be.