Understanding The Stack In Embedded Programming

What is a Variable?

Variables can generally be subdivided as shown below:

Understanding The Stack In Embedded Programming

The focus of this section is to help everyone understand the “stack” in the memory model, temporarily ignoring the situation of “static variables” and agreeing on the following:

“Global variables” are simply assumed to be “ordinary global variables”;

“Local variables” are simply assumed to be “ordinary local variables”.

How to Determine Global and Local Variables?

Simply put, a global variable is defined outside of a function, while a local variable is defined within a function. The following example illustrates how to clearly determine global and local variables:

unsigned char a; // Defined outside the function, so it is a global variable. void main() // Main function { unsigned char b; // Defined inside the function, so it is a local variable. b = a; while(1) { } }

Memory Model of Global and Local Variables

The microcontroller’s memory includes<span>ROM</span> and <span>RAM</span>. <span>ROM</span> stores instructions and some immutable constant data in the microcontroller’s program, while <span>RAM</span> stores variable data that can be changed;

In other words, both global and local variables are stored in<span>RAM</span>, but even though they are both stored in <span>RAM</span>, there are significant differences in the memory models between global and local variables.

Therefore, two different<span>RAM</span> areas are allocated: the area occupied by global variables is called the global data area, and the area occupied by local variables is called the stack.

What are the Essential Differences in Their Memory Models?

The global data area is like your own room at home; it is unique, and the address of a room can only be occupied by you (assuming you are still single), and it is permanent (sorry), so each global variable has a unique corresponding RAM address that cannot be duplicated.

The stack is like an inn, where different people stay every night throughout the year. Each person’s stay has a time limit and is not permanent; the address of a room may be occupied by different people throughout the year, so it is not unique.

The global variables in the global data area have permanent ownership, while the local variables in the stack can only temporarily reside in the inn, and their addresses are not unique and have a time limit.

The stack is shared among all local variables in the functions of the program. When a function is called, each local variable inside that function is allocated to a certain <span>RAM</span> address in the stack. After the function call ends, that local variable becomes invalid.

Thus, the corresponding RAM space of the stack is reclaimed to be used for the local variables of the next called function.

Using the “Inn” as a Metaphor for the Local Variables in the “Stack”.

void function(void); // Declaration of the sub-function void function(void) // Definition of the sub-function { unsigned char a; // Local variable a = 1; } void main() // Main function { function(); // Call to the sub-function }

We see that the microcontroller executes from the main function downwards. It first encounters the call to the sub-function <span>function()</span>, so it jumps to the definition of <span>function()</span> to start execution. At this point, the local variable a begins to be allocated at a certain address in the <span>RAM</span> in the “stack area”, similar to being assigned to a room in an inn.

After the microcontroller completes the sub-function <span>function()</span>, the local variable a at the address allocated in the <span>RAM</span> of the <span>stack</span><span> is reclaimed, and the local variable a disappears. The reclaimed </span><code><span>RAM</span> address may be reassigned by the system to other local variables of called functions.

At this point, it is as if you left the inn, and from then on, you have no connection with that room in the inn; the room you occupied will be reassigned to other guests.

The scope of global variables is permanent and unrestricted, while the scope of local variables is limited to the internal range of the function they are in. The global variables in the <span>global data area</span><span> are permanent private houses, while the local variables in the </span><code><span>stack</span><span> are temporary lodgings.</span>

Summary as follows

  1. Every time a new global variable is defined, it means an additional new <span>RAM</span> memory cost. However, each time a local variable is defined, as long as the total number of local variables defined within the function does not exceed the microcontroller’s <span>stack</span><span> area, the current local variable does not incur additional </span><code><span>RAM</span> memory cost, because local variables temporarily borrow from the <span>stack</span><span>, which is a public area that can be reused to serve several different local variables of functions.</span>

  2. Every time a function is executed in the microcontroller, local variables are initialized and changed, while global variables are not initialized; global variables retain the last changed value.

What are the Common Questions?

Who is in charge of allocating the global data area and stack area, and how is it allocated?

It is automatically allocated by the C compiler. As for how it is allocated, the C compiler will have a default ratio for allocation, and we generally do not need to worry about it.

The stack area is temporarily borrowed; when a sub-function is called, its internal local variables will be “temporarily” allocated to a certain address in the “stack” area. So the question arises: who is in charge of the allocation work in the “stack area”?

When the microcontroller is powered on and starts running the program, the compiler is no longer in effect; the allocation of the “stack area” for the internal local variables of functions is indeed done by the C compiler, but this is before the microcontroller is powered on.

The C compiler plans all the allocation work for the local variables of all functions in advance, specifying which local variable of a function should be allocated to which address in the “stack area” once that function is called. The C compiler finishes its job after detailing these “future matters”.

When the microcontroller is powered on and starts working, although the C compiler is no longer present, the microcontroller strictly follows the instructions left by the C compiler to start working and allocating the “stack area”. Therefore, the “temporary allocation” of the “stack area” is not strictly a “temporary allocation” in the true sense.

If the total number of local variables defined within a function exceeds the microcontroller’s “stack” area RAM size, will the consequences be serious?

This situation is called stack overflow. The program may exhibit inexplicable anomalies, and the consequences can be particularly severe.

To avoid this situation, when writing programs, functions should generally not define large arrays as local variables; the number of local variables should not be too many or too large, especially avoiding the situation of defining large array local variables.

Large arrays should be defined as global variables or defined as static local variables.

Some C compilers may kindly remind you when encountering a “stack overflow” situation, preventing you from compiling, but some C compilers may not give you a reminder. Therefore, everyone should be wary of stack overflow when writing functions in projects.

Priority of Global and Local Variables

As mentioned earlier, the scope of global variables is permanent and unrestricted, while the scope of local variables is limited to the internal range of the function they are in.

So the question arises: if a local variable and a global variable have the same name, which variable is executed in the function? Is it the local variable or the global variable?

This question involves priority.

Note: When faced with a local variable and a global variable with the same name, the variable executed within the function is the local variable, meaning that local variables have a higher priority than global variables within the function.

Let’s Look at Some Examples

See the first example below

unsigned char a = 5; // Here, the first a is a global variable void main() // Main function { unsigned char a = 2; // Here, the second a is a local variable, which has the same name as the first global variable print(a); // Send a to the computer's serial port assistant software to observe while(1) { } }

The correct answer is 2. The local variable within the function has a higher priority than the global variable.

Although the two a’s are the same name, their memory models are different; the first global variable a is allocated in the <span>global data area</span><span>, which has a unique address, while the second local variable a is allocated in the temporary </span><code><span>stack area</span><span>, residing inside the main function.</span>

Now let’s look at the second example

void function(void); // Function declaration unsigned char a = 5; // Here, the first a is a global variable void function(void) // Function definition { unsigned char a = 3; // Here, the second a is a local variable. } void main() // Main function { unsigned char a = 2; // Here, the third a is also a local variable. function(); // Call to the sub-function print(a); // Send a to the computer's serial port assistant software to observe. while(1) { } }

The correct answer is 2. Because the function <span>function</span> is called, and after it ends, the <span>print(a)</span> statement is executed, which means the local variable (the second local variable a) inside the function does not exist when executing <span>print(a)</span>, so <span>print(a)</span> refers to the third local variable a (the one defined in the main function).

Now let’s look at the third example

void function(void); // Function declaration unsigned char a = 5; // Here, the first a is a global variable void function(void) // Function definition { unsigned char a = 3; // Here, the second a is a local variable } void main() // Main function { function(); // Call to the sub-function print(a); // Send a to the computer's serial port assistant software to observe while(1) { } }

The correct answer is 5. After the function <span>function</span> is called, it ends, and then <span>print(a)</span> is executed, which means that the local variable (the second local variable a) inside the function does not exist anymore.

At this point, since there are no local variables defined in the main function, the variable <span>a</span> in <span>function(a)</span> must be the first global variable a (the one defined outside the main function).

Finally

After reading this article, I believe everyone has a basic understanding of the stack. In embedded programming, we must always be cautious to avoid stack overflow; please feel free to point out any errors.

Understanding The Stack In Embedded Programming

Microsoft Forms Rust Developer Team

2021-02-03

Understanding The Stack In Embedded Programming

Milestone! VS Code Go Language Extension Will Enable gopls by Default

2021-02-04

Understanding The Stack In Embedded Programming

Godot 4.0 CPU and GPU Optimization Significantly Reduces Rendering Time

2021-02-04

Understanding The Stack In Embedded Programming

Understanding The Stack In Embedded Programming

Understanding The Stack In Embedded ProgrammingIf you find this helpful, please click to see it!

Leave a Comment